From d4b7bc642fa3305252f14616d959516c09ba6ae4 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 19 Oct 2026 07:08:29 +0000 Subject: [PATCH 1/9] fix(projectgraph): end the Rust link step on a use of its own name (FOU-545) A Rust file with use smallvec::smallvec made the link step die with a stack overflow. The use binds smallvec. The walk of the path read the first smallvec through the same binding, and that binding led back to the same use, with no end. Rust resolves the first name of a use path against the crates and the modules, never against the binding of the same use. resolveRust now keeps the paths that each file resolves now. A repeat of a path gives no answer, so the walk tries the next candidate and ends. The path then names the crate, as smallvec//smallvec. The crate selectors 0.22.0 showed a second cycle of the same kind. An impl of Display for the enum Component holds use self::Component::*. The base Display can resolve through that wildcard, which asks for the external base of Component again. findExternalBase now keeps the classes that it reads, and a repeat has no external base. A link test runs use smallvec::smallvec, use a::a, use a::a::a and use self::a as a, and a second one the wildcard of the class in its impl. Each one overflowed the stack before the fix. The case rust/use-crate-name links a crate with the use and finds SmallVec::new. No graph of an existing case changes, so the version stays, and the hash rows of 11/11 take the new case. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01PFQbqPqd9xn1fT6hBsyoUm Signed-off-by: Claude --- projectgraph/cha.go | 10 +++ projectgraph/link.go | 59 +++++++------ projectgraph/rust.go | 27 ++++-- projectgraph/rust_test.go | 85 +++++++++++++++++++ .../cases/rust/use-crate-name/expect.yaml | 49 +++++++++++ .../rust/use-crate-name/project/Cargo.toml | 7 ++ .../rust/use-crate-name/project/src/main.rs | 7 ++ .../rust/use-crate-name/project/src/store.rs | 5 ++ test/projectgraph/version_test.go | 4 +- 9 files changed, 218 insertions(+), 35 deletions(-) create mode 100644 projectgraph/rust_test.go create mode 100644 test/projectgraph/cases/rust/use-crate-name/expect.yaml create mode 100644 test/projectgraph/cases/rust/use-crate-name/project/Cargo.toml create mode 100644 test/projectgraph/cases/rust/use-crate-name/project/src/main.rs create mode 100644 test/projectgraph/cases/rust/use-crate-name/project/src/store.rs diff --git a/projectgraph/cha.go b/projectgraph/cha.go index 376c4340..c26ff585 100644 --- a/projectgraph/cha.go +++ b/projectgraph/cha.go @@ -185,7 +185,17 @@ func (b *builder) externalBase(class string) (string, bool) { return base, base != "" } +// findExternalBase searches the bases of the MRO of a class for an external +// one. The name of a base can resolve through a wildcard import of the class +// itself, as use self::Component::* in an impl of Component in Rust, and +// that lookup asks for the external base of the same class again. A class +// that the search reads now has no external base, so the search ends. func (b *builder) findExternalBase(class string) (string, bool) { + if b.findingBases[class] { + return "", false + } + b.findingBases[class] = true + defer delete(b.findingBases, class) for _, c := range b.mro(class) { for _, rec := range b.records(c) { for _, base := range b.bases(rec) { diff --git a/projectgraph/link.go b/projectgraph/link.go index 790fa88b..9890f31d 100644 --- a/projectgraph/link.go +++ b/projectgraph/link.go @@ -131,9 +131,11 @@ type builder struct { groupMembers map[string]map[string]string nodesByParent map[string][]string // groupRecords holds the class records of each group of aliases, and - // externalBases the first external base of each class, or "". + // externalBases the first external base of each class, or "", and + // findingBases the classes whose external base a search reads now. groupRecords map[string][]classRecord externalBases map[string]string + findingBases map[string]bool // state holds the state of each language that keeps one, as the crates // of Rust. langState reads it. state map[langcode.Code]any @@ -248,33 +250,34 @@ func newTables(indexes []*fileindex.Index, opts Options) *builder { Nodes: make(map[string]*Node, defs), Files: make(map[string]fileindex.FileStatus, len(indexes)), }, - byPath: make(map[string]*fileindex.Index, len(indexes)), - scopes: make(map[string]*scope, len(indexes)), - edges: newEdgeSet(sites), - unresolved: map[UnresolvedSite]bool{}, - truncations: map[Truncation]bool{}, - names: map[nameKey]resolution{}, - visiting: map[nameKey]bool{}, - bindings: map[string]string{}, - classes: map[string]classRecord{}, - defs: make(map[string]*fileindex.Definition, defs), - reading: map[string]bool{}, - state: map[langcode.Code]any{}, - units: map[string]*unit{}, - unitOf: map[string]*unit{}, - spaces: map[string]*space{}, - origins: map[string][]origin{}, - provisions: map[provisionKey][]provision{}, - linked: map[string]bool{}, - named: map[string]bool{}, - namespaces: map[langcode.Code]*namespaces{}, - loads: map[string]bool{}, - mros: map[string][]string{}, - aliases: map[string][]string{}, - narrowed: map[Site][]string{}, - structural: map[siteCall]bool{}, - test: map[string]bool{}, - rules: roots.Merge(roots.Default(), opts.Rules), + byPath: make(map[string]*fileindex.Index, len(indexes)), + scopes: make(map[string]*scope, len(indexes)), + edges: newEdgeSet(sites), + unresolved: map[UnresolvedSite]bool{}, + truncations: map[Truncation]bool{}, + names: map[nameKey]resolution{}, + visiting: map[nameKey]bool{}, + bindings: map[string]string{}, + classes: map[string]classRecord{}, + defs: make(map[string]*fileindex.Definition, defs), + reading: map[string]bool{}, + findingBases: map[string]bool{}, + state: map[langcode.Code]any{}, + units: map[string]*unit{}, + unitOf: map[string]*unit{}, + spaces: map[string]*space{}, + origins: map[string][]origin{}, + provisions: map[provisionKey][]provision{}, + linked: map[string]bool{}, + named: map[string]bool{}, + namespaces: map[langcode.Code]*namespaces{}, + loads: map[string]bool{}, + mros: map[string][]string{}, + aliases: map[string][]string{}, + narrowed: map[Site][]string{}, + structural: map[siteCall]bool{}, + test: map[string]bool{}, + rules: roots.Merge(roots.Default(), opts.Rules), } } diff --git a/projectgraph/rust.go b/projectgraph/rust.go index a589c0dc..429a751b 100644 --- a/projectgraph/rust.go +++ b/projectgraph/rust.go @@ -73,11 +73,13 @@ var rustImplicit = map[string]bool{ // toolchain, which no crate of the project defines. var rustRoots = map[string]bool{"std": true, "core": true, "alloc": true} -// rustState is the state of Rust in a build: the crates, and the module -// path of each Rust file. +// rustState is the state of Rust in a build: the crates, the module path +// of each Rust file, and the paths that a resolution reads now, by the file +// that writes them. type rustState struct { - cargo *modules.Cargo - modules map[string]string + cargo *modules.Cargo + modules map[string]string + resolving map[[2]string]bool } func (b *builder) rustState() *rustState { @@ -91,6 +93,7 @@ func (b *builder) addRustUnits() { s.cargo = modules.NewCargo(b.project.files, b.opts.Root) b.problems = append(b.problems, s.cargo.Problems()...) s.modules = map[string]string{} + s.resolving = map[[2]string]bool{} files := map[string][]string{} for _, p := range b.paths { if ix := b.byPath[p]; ix != nil && ix.Language == langcode.Rust { @@ -173,8 +176,22 @@ func joinPath(module string, parts []string) string { // resolveRust resolves a path that a file of the module writes to a // definition or a module of the project. +// +// A use binds the last name of its path, and the walk of the path can read +// that binding, as the first smallvec of use smallvec::smallvec. Rust +// resolves the first name of a use path against the crates and the modules, +// and never against the binding of the same use. So a path that the file +// resolves now gives no answer on a repeat, and the walk tries the next +// candidate. Without the check the walk does not end. func (b *builder) resolveRust(importer, written string) (string, bool) { - for _, p := range b.rustPaths(b.rustState().modules[importer], written) { + s := b.rustState() + k := [2]string{importer, written} + if s.resolving[k] { + return "", false + } + s.resolving[k] = true + defer delete(s.resolving, k) + for _, p := range b.rustPaths(s.modules[importer], written) { if key, ok := b.resolveQualified(langcode.Rust, p); ok { return key, true } diff --git a/projectgraph/rust_test.go b/projectgraph/rust_test.go new file mode 100644 index 00000000..4c82e52b --- /dev/null +++ b/projectgraph/rust_test.go @@ -0,0 +1,85 @@ +package projectgraph + +import ( + "context" + "testing" + "testing/fstest" + + "github.com/safedep/code/fileindex" + "github.com/safedep/code/langcode" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// rustUse is src/main.rs of the crate app with one use and a call of the +// name that the use binds. +func rustUse(module, alias string) *fileindex.Index { + const p = "src/main.rs" + ns := fileindex.Namespace(langcode.Rust, module) + return &fileindex.Index{ + Version: fileindex.Version, + Path: p, + Language: langcode.Rust, + Imports: []fileindex.Import{{Line: 1, Module: module, Alias: alias, Kind: fileindex.ImportStatic, Namespace: ns}}, + Definitions: []fileindex.Definition{ + def(fileindex.DefModule, p, 1), + def(fileindex.DefFunction, fileindex.Join(p, "main"), 2), + }, + Calls: []fileindex.Call{{Caller: fileindex.Join(p, "main"), Line: 2, Column: 13, Callee: alias, Targets: []string{ns}, Method: alias, Kind: fileindex.CallCall}}, + } +} + +func TestBuildRustUseOfItsOwnBinding(t *testing.T) { + root := fstest.MapFS{"Cargo.toml": {Data: []byte("[package]\nname = \"app\"\nversion = \"0.1.0\"\n")}} + tests := []struct { + name string + module string + alias string + edge string + }{ + {"use smallvec::smallvec", "smallvec::smallvec", "smallvec", "src/main.rs//main -> smallvec//smallvec direct rust.external"}, + {"use a::a", "a::a", "a", "src/main.rs//main -> a//a direct rust.external"}, + {"use a::a::a", "a::a::a", "a", "src/main.rs//main -> a//a//a direct rust.external"}, + // The module defines no a, so the program does not compile. The + // test checks that the link step ends. + {"use self::a as a", "self::a", "a", ""}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + g, err := Build(context.Background(), []*fileindex.Index{rustUse(tt.module, tt.alias)}, Options{Root: root}) + require.NoError(t, err) + if tt.edge != "" { + assert.Contains(t, edgeStrings(g), tt.edge) + } + }) + } +} + +// TestBuildRustWildcardUseOfTheClassInItsImpl links an impl of a trait for +// an enum whose method imports the variants of the enum with a wildcard, +// as use self::Component::* in impl Display for Component. The base +// Display can resolve through that wildcard, which leads back to the same +// class. +func TestBuildRustWildcardUseOfTheClassInItsImpl(t *testing.T) { + const p = "src/main.rs" + class := fileindex.Join(p, "Component") + ix := &fileindex.Index{ + Version: fileindex.Version, + Path: p, + Language: langcode.Rust, + Imports: []fileindex.Import{ + {Line: 1, Module: "std::fmt::Display", Alias: "Display", Kind: fileindex.ImportStatic, Namespace: "std//fmt//Display"}, + {Line: 9, Module: "self::Component", Wildcard: true, Kind: fileindex.ImportStatic, Namespace: "self//Component"}, + }, + Definitions: []fileindex.Definition{ + def(fileindex.DefModule, p, 1), + def(fileindex.DefClass, class, 7), + def(fileindex.DefMethod, fileindex.Join(class, "fmt"), 8), + }, + Classes: []fileindex.Class{{Key: class, Bases: []string{"Display"}, Methods: []string{fileindex.Join(class, "fmt")}}}, + } + root := fstest.MapFS{"Cargo.toml": {Data: []byte("[package]\nname = \"app\"\nversion = \"0.1.0\"\n")}} + g, err := Build(context.Background(), []*fileindex.Index{ix}, Options{Root: root}) + require.NoError(t, err) + assert.Equal(t, NodeClass, g.Nodes[class].Kind) +} diff --git a/test/projectgraph/cases/rust/use-crate-name/expect.yaml b/test/projectgraph/cases/rust/use-crate-name/expect.yaml new file mode 100644 index 00000000..113d2081 --- /dev/null +++ b/test/projectgraph/cases/rust/use-crate-name/expect.yaml @@ -0,0 +1,49 @@ +source: safedep:hand +description: >- + main.rs has use smallvec::smallvec, a use whose last name is the name of its crate. Rust resolves the first + smallvec against the crates, not against the binding of the same use, so the link step ends. main calls + store::save, and save calls SmallVec::new of the crate smallvec. No call names the macro smallvec. +roots: + - key: src/main.rs + kind: main + rule: rust.main + - key: src/main.rs//main + kind: main + rule: rust.main +exact_edges: true +edges: + - from: src/main.rs//main + to: src/store.rs//save + kind: direct + confidence: high + site: src/main.rs:6 + - from: src/store.rs//save + to: smallvec//SmallVec + kind: reference + confidence: low + site: src/store.rs:4 + - from: src/store.rs//save + to: smallvec//SmallVec//new + kind: direct + confidence: high + site: src/store.rs:4 +unresolved: [] +results: + - target: smallvec//SmallVec//new + status: found + path: + - key: src/main.rs//main + - key: src/store.rs//save + site: src/main.rs:6 + kind: direct + confidence: high + - key: smallvec//SmallVec//new + site: src/store.rs:4 + kind: direct + confidence: high + call_sites: + - caller: src/store.rs//save + site: src/store.rs:4 + reached: true + - target: smallvec//smallvec + status: no-such-target diff --git a/test/projectgraph/cases/rust/use-crate-name/project/Cargo.toml b/test/projectgraph/cases/rust/use-crate-name/project/Cargo.toml new file mode 100644 index 00000000..dbeadb3d --- /dev/null +++ b/test/projectgraph/cases/rust/use-crate-name/project/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "app" +version = "0.1.0" +edition = "2021" + +[dependencies] +smallvec = "1.13" diff --git a/test/projectgraph/cases/rust/use-crate-name/project/src/main.rs b/test/projectgraph/cases/rust/use-crate-name/project/src/main.rs new file mode 100644 index 00000000..68bde29f --- /dev/null +++ b/test/projectgraph/cases/rust/use-crate-name/project/src/main.rs @@ -0,0 +1,7 @@ +use smallvec::smallvec; + +mod store; + +fn main() { + store::save(); +} diff --git a/test/projectgraph/cases/rust/use-crate-name/project/src/store.rs b/test/projectgraph/cases/rust/use-crate-name/project/src/store.rs new file mode 100644 index 00000000..9d51d0e1 --- /dev/null +++ b/test/projectgraph/cases/rust/use-crate-name/project/src/store.rs @@ -0,0 +1,5 @@ +use smallvec::SmallVec; + +pub fn save() { + let _buf: SmallVec<[u8; 4]> = SmallVec::new(); +} diff --git a/test/projectgraph/version_test.go b/test/projectgraph/version_test.go index c8756b6c..2f2fe985 100644 --- a/test/projectgraph/version_test.go +++ b/test/projectgraph/version_test.go @@ -24,7 +24,7 @@ import ( var graphHashes = map[string]string{ "10/10": "fa32ae11c72bdf01c93d4b7afe3564b1d59b2436306e3b3503f01fc4c308e5aa", "11/10": "421c6441093c8f82e4db8a2342884b5e42faf360951985d3600d99c96202625b", - "11/11": "0638d53222abbdbdbe81e09f4cdc3fb8f49b644e3576a7bfec2bfe68807b6392", + "11/11": "19e0527969cf2702f5abfab8234310b254ac25a6a539ab6e86cf6febabfb8757", } // resultHashes records the hash of the reach results of every case for @@ -33,7 +33,7 @@ var graphHashes = map[string]string{ var resultHashes = map[string]string{ "11/10/1": "4beb6248ef1b8dc3572939a41d1aea11c6504f5007a5f24fd1261db026708186", "11/11/1": "4beb6248ef1b8dc3572939a41d1aea11c6504f5007a5f24fd1261db026708186", - "11/11/2": "2b48d96ff43fe68aa70967dab6cd7ac7b2f93c9e247b45fec0a472c869781448", + "11/11/2": "58d8fd9c5b67bf02a44778a5173828515c9424a1fddd9ff3f644d8fc13ebcfbb", } // summaryHashes records the hash of the golden summaries of the cases for From 955e75a616469958a867d0977a7e1b087fe92e6a Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 19 Oct 2026 07:35:04 +0000 Subject: [PATCH 2/9] test(projectgraph): measure how often a Go or Rust method target is uncertain (FOU-542) The word "often" had no number behind it. This change gives the number and the rows that later fixes must flip. receivers_test.go has one row per form of the receiver of a method call of a dependency type, for Go and for Rust: a declared variable, a parameter, a struct field, a first-party return, an external return, a multi-return, a chained call and an interface return, and for Rust a reference and generic parameter, an associated function, a clone, a trait object and a generic bound. Small synthetic units under testdata/receivers give the dependency code. Each row names its answer. A row that fails today names the issue that fixes it, FOU-543, FOU-544 or FOU-546, and its answer today, so go test stays green. methods_test.go measures real projects under the projectgraph tag: 10 Go and 10 Rust projects that depend on a module or a crate with method symbols in the Go vulndb or in RustSec. corpus.txt pins each project and each dependency, and advisories.json pins the advisories. Every symbol of an advisory that affects a pinned dependency is a target, and the code of the dependency is a unit. scripts/fetch_method_corpus.sh fetches the code. The test counts found, not-found, uncertain by cause and no-such-target, per language and per symbol kind. report.md has the first numbers. 0.227 (294/1298) of the Go method targets and 0.342 (13/38) of the Rust ones end as unknown-receiver. The first run found a second stack overflow in the Rust link step, in selectors 0.22.0. The FOU-545 commit holds that fix. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01PFQbqPqd9xn1fT6hBsyoUm Signed-off-by: Claude --- scripts/fetch_method_corpus.sh | 30 + test/projectgraph/README.md | 29 +- test/projectgraph/methods_test.go | 290 ++ .../public/methods/advisories.json | 4296 +++++++++++++++++ test/projectgraph/public/methods/corpus.txt | 23 + test/projectgraph/receivers_test.go | 387 ++ test/projectgraph/report.md | 73 + .../testdata/receivers/go/dep/dep.go | 34 + .../testdata/receivers/go/dep/go.mod | 3 + .../testdata/receivers/rust/dep/Cargo.toml | 4 + .../testdata/receivers/rust/dep/src/lib.rs | 55 + 11 files changed, 5222 insertions(+), 2 deletions(-) create mode 100755 scripts/fetch_method_corpus.sh create mode 100644 test/projectgraph/methods_test.go create mode 100644 test/projectgraph/public/methods/advisories.json create mode 100644 test/projectgraph/public/methods/corpus.txt create mode 100644 test/projectgraph/receivers_test.go create mode 100644 test/projectgraph/testdata/receivers/go/dep/dep.go create mode 100644 test/projectgraph/testdata/receivers/go/dep/go.mod create mode 100644 test/projectgraph/testdata/receivers/rust/dep/Cargo.toml create mode 100644 test/projectgraph/testdata/receivers/rust/dep/src/lib.rs diff --git a/scripts/fetch_method_corpus.sh b/scripts/fetch_method_corpus.sh new file mode 100755 index 00000000..7597a726 --- /dev/null +++ b/scripts/fetch_method_corpus.sh @@ -0,0 +1,30 @@ +#!/bin/bash +# Fetch the projects and the dependencies of the method measure into the +# directory of the first argument. Go modules come from the module proxy +# into /gomod, and crates from crates.io into /crates. The list +# is test/projectgraph/public/methods/corpus.txt. Point +# PROJECTGRAPH_METHOD_CORPUS at the directory to measure them. +set -euo pipefail + +dir="${1:?usage: fetch_method_corpus.sh }" +list="$(dirname "$0")/../test/projectgraph/public/methods/corpus.txt" +mkdir -p "$dir/gomod" "$dir/crates" +dir="$(cd "$dir" && pwd)" + +grep -v '^#' "$list" | while read -r language _ project deps; do + for item in $project $deps; do + case "$language" in + go) + GOFLAGS=-modcacherw GOMODCACHE="$dir/gomod" go mod download "$item" + ;; + rust) + name="${item%@*}" + version="${item#*@}" + if [ ! -d "$dir/crates/$name-$version" ]; then + curl -sSfL "https://static.crates.io/crates/$name/$name-$version.crate" | tar -xz -C "$dir/crates" + fi + ;; + esac + done +done +echo "$dir" diff --git a/test/projectgraph/README.md b/test/projectgraph/README.md index 62b56b4b..bc78a905 100644 --- a/test/projectgraph/README.md +++ b/test/projectgraph/README.md @@ -5,8 +5,8 @@ This directory tests the index step, the link step and the reach query of | Part | Files | Runs in | | --- | --- | --- | -| Hand-written cases | `cases///`, `cases_test.go`, `workers_test.go` (one output for any number of workers), `version_test.go` (the hash of the graphs and the summaries per version) | `go test ./...` | -| Public sets | `public/`, `public_test.go`, `govulncheck_test.go` | `go test -tags projectgraph` | +| Hand-written cases | `cases///`, `cases_test.go`, `receivers_test.go` (the forms of a method receiver), `workers_test.go` (one output for any number of workers), `version_test.go` (the hash of the graphs and the summaries per version) | `go test ./...` | +| Public sets | `public/`, `public_test.go`, `govulncheck_test.go`, `methods_test.go` | `go test -tags projectgraph` | | Benchmark | `bench_test.go`, `synth_test.go`, `synth_languages_test.go` (the C#, PHP, Ruby and Rust generators) | `go test -tags projectgraph -bench .` | `helpers_test.go` holds the index and link helpers that every part uses. @@ -116,6 +116,31 @@ go test -tags projectgraph -run TestGovulncheckTraces -count=1 -v ./test/project Set `PROJECTGRAPH_PUBLIC_DUMP` to a case name, as `classes/self_call`, or to `all`, to log the edges, the extra pairs and the missed pairs of the case. +## Run the method measure + +`methods_test.go` measures how often a target of a vulnerable function or +method of a dependency ends uncertain on real projects. +`public/methods/corpus.txt` lists the projects and the dependencies, and +`public/methods/advisories.json` holds their advisories from the Go vulndb +and from RustSec through OSV. The code is not in the repository, so fetch +it first: + +```bash +export PROJECTGRAPH_METHOD_CORPUS=$(bash scripts/fetch_method_corpus.sh "$(mktemp -d)") +go test -tags projectgraph -run TestMethodTargets -count=1 -v ./test/projectgraph/ +``` + +The test logs the count of each answer per language and per symbol kind, +and the same per project. Set `PROJECTGRAPH_METHOD_REPORT` to a file path +to write the block to that file. `report.md` says how the corpus was +chosen and what each cause means. + +`receivers_test.go` holds one row per form of the receiver of a method +call, for Go and for Rust, against the small dependency units under +`testdata/receivers`. It runs in `go test ./...`. A row that the link step +does not answer yet names the issue that fixes it and its answer before +that fix, so the test stays green until the fix changes the answer. + ## Run the benchmark ```bash diff --git a/test/projectgraph/methods_test.go b/test/projectgraph/methods_test.go new file mode 100644 index 00000000..5b2b5ea7 --- /dev/null +++ b/test/projectgraph/methods_test.go @@ -0,0 +1,290 @@ +//go:build projectgraph + +package projectgraph + +import ( + "bufio" + "context" + "encoding/json" + iofs "io/fs" + "maps" + "os" + "path/filepath" + "slices" + "strconv" + "strings" + "testing" + "unicode" + "unicode/utf8" + + "github.com/safedep/code/fileindex" + codefs "github.com/safedep/code/fs" + "github.com/safedep/code/lang" + "github.com/safedep/code/plugin/indexer" + "github.com/safedep/code/projectgraph" + "github.com/safedep/code/projectgraph/reach" + "github.com/stretchr/testify/require" + "golang.org/x/mod/module" + "golang.org/x/mod/semver" +) + +// methodCorpusEnv names the directory that scripts/fetch_method_corpus.sh +// fills with the projects and the dependencies of the method measure. +const methodCorpusEnv = "PROJECTGRAPH_METHOD_CORPUS" + +// methodsDir holds the list of the projects and the advisories of their +// dependencies, from the Go vulndb and from RustSec through OSV. +var methodsDir = filepath.Join("public", "methods") + +// methodProject is one line of corpus.txt. +type methodProject struct { + language, name, source string + deps []string +} + +// advisory is one advisory of a package: the events of its affected +// ranges, and the symbols of Go or the functions of Rust that it names. +type advisory struct { + ID string `json:"id"` + Ranges []struct { + Introduced string `json:"introduced"` + Fixed string `json:"fixed"` + } `json:"ranges"` + Imports []struct { + Path string `json:"path"` + Symbols []string `json:"symbols"` + } `json:"imports"` + Functions []string `json:"functions"` +} + +// affects reports whether the ranges of the advisory hold the version. +func (a advisory) affects(version string) bool { + v := "v" + strings.TrimPrefix(version, "v") + inside := false + for _, e := range a.Ranges { + switch { + case e.Introduced == "0", e.Introduced != "" && semver.Compare("v"+e.Introduced, v) <= 0: + inside = true + case e.Fixed != "" && semver.Compare("v"+e.Fixed, v) <= 0: + inside = false + } + } + return inside +} + +// methodTarget is a target of the measure and the kind of its symbol. +type methodTarget struct { + key string + kind string +} + +// symbolKinds name the kind of a symbol: a function, a method of a type, +// or another name, as a type that RustSec lists. +const ( + kindFunction = "function" + kindMethod = "method" + kindOther = "other" +) + +func readMethodCorpus(t *testing.T) ([]methodProject, map[string]map[string][]advisory) { + f, err := os.Open(filepath.Join(methodsDir, "corpus.txt")) + require.NoError(t, err) + defer func() { require.NoError(t, f.Close()) }() + var projects []methodProject + scanner := bufio.NewScanner(f) + for scanner.Scan() { + fields := strings.Fields(scanner.Text()) + if len(fields) == 0 || strings.HasPrefix(fields[0], "#") { + continue + } + require.GreaterOrEqual(t, len(fields), 3, scanner.Text()) + projects = append(projects, methodProject{language: fields[0], name: fields[1], source: fields[2], deps: fields[3:]}) + } + require.NoError(t, scanner.Err()) + + data, err := os.ReadFile(filepath.Join(methodsDir, "advisories.json")) + require.NoError(t, err) + var advisories map[string]map[string][]advisory + require.NoError(t, json.Unmarshal(data, &advisories)) + return projects, advisories +} + +// methodTargets returns the symbols that the advisories of a dependency +// name at its version, each once. An advisory with no symbol list covers +// the whole package, which the measure cannot list, and a Rust name with +// type arguments is no key. +func methodTargets(language, dep string, advisories map[string]map[string][]advisory) []methodTarget { + name, version, _ := strings.Cut(dep, "@") + seen := map[string]bool{} + var out []methodTarget + add := func(key, kind string) { + if !seen[key] { + seen[key] = true + out = append(out, methodTarget{key: key, kind: kind}) + } + } + if language == "go" { + for _, a := range advisories["go"][name] { + if !a.affects(version) { + continue + } + for _, imp := range a.Imports { + for _, sym := range imp.Symbols { + kind := kindFunction + if strings.Contains(sym, ".") { + kind = kindMethod + } + add(fileindex.Join(fileindex.Namespace("go", imp.Path), strings.Split(sym, ".")...), kind) + } + } + } + return out + } + for _, a := range advisories["cargo"][name] { + if !a.affects(version) { + continue + } + for _, fn := range a.Functions { + if strings.ContainsAny(fn, "<> ") { + continue + } + segs := strings.Split(fn, "::") + kind := kindFunction + switch { + case len(segs) > 2 && startsUpper(segs[len(segs)-2]): + kind = kindMethod + case startsUpper(segs[len(segs)-1]): + kind = kindOther + } + add(strings.Join(segs, fileindex.Separator), kind) + } + } + return out +} + +func startsUpper(s string) bool { + r, _ := utf8.DecodeRuneInString(s) + return unicode.IsUpper(r) +} + +// methodDir returns the directory of a project or a dependency in the +// corpus, and its unit. +func methodDir(t *testing.T, corpus, language, item string) (string, string) { + name, version, _ := strings.Cut(item, "@") + if language == "go" { + escaped, err := module.EscapePath(name) + require.NoError(t, err) + return filepath.Join(corpus, "gomod", filepath.FromSlash(escaped)+"@"+version), "go:" + item + } + return filepath.Join(corpus, "crates", name+"-"+version), "cargo:" + item +} + +// nonTestRoots returns the keys of the roots of the graph that are not +// test roots, as the search of a query without tests starts from them. +func nonTestRoots(g *projectgraph.Graph) []string { + var keys []string + for _, r := range g.Roots { + if r.Kind != projectgraph.RootTest { + keys = append(keys, r.Key) + } + } + return keys +} + +// methodCounts holds the outcomes of the targets by language and kind. +type methodCounts map[[2]string]map[string]int + +func (c methodCounts) add(language, kind, outcome string) { + k := [2]string{language, kind} + if c[k] == nil { + c[k] = map[string]int{} + } + c[k][outcome]++ +} + +// rows writes one line per language and kind with the count of each +// outcome, and the share of the method targets whose cause is an unknown +// receiver. +func (c methodCounts) rows() []string { + keys := slices.SortedFunc(maps.Keys(c), func(a, b [2]string) int { + return strings.Compare(a[0]+"/"+a[1], b[0]+"/"+b[1]) + }) + var rows []string + for _, k := range keys { + counts := c[k] + total := 0 + for _, n := range counts { + total += n + } + parts := []string{"language=" + k[0], "kind=" + k[1], "targets=" + strconv.Itoa(total)} + for _, o := range slices.Sorted(maps.Keys(counts)) { + parts = append(parts, o+"="+strconv.Itoa(counts[o])) + } + rows = append(rows, strings.Join(parts, " ")) + if k[1] == kindMethod { + rows = append(rows, " unknown-receiver="+ratio{hit: counts["uncertain/"+string(fileindex.UnresolvedUnknownReceiver)], all: total}.String()) + } + } + return rows +} + +// TestMethodTargets measures how often a target of a vulnerable function +// or method of a dependency of a real project ends uncertain. Each target +// is a symbol of an advisory of a dependency at its pinned version, and +// the code of each such dependency is a unit of the link step. +func TestMethodTargets(t *testing.T) { + corpus := os.Getenv(methodCorpusEnv) + if corpus == "" { + t.Skipf("%s is not set: run scripts/fetch_method_corpus.sh", methodCorpusEnv) + } + languages, err := lang.AllLanguages() + require.NoError(t, err) + projects, advisories := readMethodCorpus(t) + + counts := methodCounts{} + var lines []string + for _, p := range projects { + dir, _ := methodDir(t, corpus, p.language, p.source) + cfg := codefs.LocalFileSystemConfig{AppDirectories: []string{dir}, ImportUnits: map[string]string{}, ExcludePatterns: codefs.DefaultSkipPatterns(dir)} + units := map[string]iofs.FS{} + var targets []methodTarget + for _, dep := range p.deps { + depDir, unit := methodDir(t, corpus, p.language, dep) + cfg.ImportDirectories = append(cfg.ImportDirectories, depDir) + cfg.ImportUnits[depDir] = unit + cfg.ExcludePatterns = append(cfg.ExcludePatterns, codefs.SkipPattern(depDir)) + units[unit] = os.DirFS(depDir) + targets = append(targets, methodTargets(p.language, dep, advisories)...) + } + fsys, err := codefs.NewLocalFileSystem(cfg) + require.NoError(t, err) + indexed := indexProject(t, fsys, indexer.Options{Languages: languages, Imports: true}) + g := linkProject(t, dir, indexed, projectgraph.Options{TestPaths: fileindex.DefaultTestPaths, Units: units}) + + keys := make([]reach.Target, 0, len(targets)) + for _, mt := range targets { + target, err := reach.TargetByKey(mt.key) + require.NoError(t, err, mt.key) + keys = append(keys, target) + } + results, err := reach.Reach(context.Background(), g, reach.Query{Targets: keys}) + require.NoError(t, err) + methods := receiverReasons(t, g, nonTestRoots(g)) + project := methodCounts{} + for i, r := range results { + o := outcome(r, methods) + counts.add(p.language, targets[i].kind, o) + project.add(p.language, targets[i].kind, o) + } + lines = append(lines, "project="+p.name+" files="+strconv.Itoa(len(g.Files))+" truncated="+strconv.Itoa(len(g.Truncated))+" problems="+strconv.Itoa(len(g.Problems))) + for _, row := range project.rows() { + lines = append(lines, " "+row) + } + } + rows := append([]string{"set=methods projects=" + strconv.Itoa(len(projects))}, counts.rows()...) + rows = append(rows, lines...) + t.Log("\n" + strings.Join(rows, "\n")) + if out := os.Getenv("PROJECTGRAPH_METHOD_REPORT"); out != "" { + require.NoError(t, os.WriteFile(out, []byte(strings.Join(rows, "\n")+"\n"), 0o644)) + } +} diff --git a/test/projectgraph/public/methods/advisories.json b/test/projectgraph/public/methods/advisories.json new file mode 100644 index 00000000..b8e158a2 --- /dev/null +++ b/test/projectgraph/public/methods/advisories.json @@ -0,0 +1,4296 @@ +{ + "cargo": { + "image": [ + { + "functions": [ + "image::hdr::HDRDecoder::read_image_transform" + ], + "id": "RUSTSEC-2019-0014", + "ranges": [ + { + "introduced": "0.10.2" + }, + { + "fixed": "0.21.3" + } + ] + }, + { + "functions": [ + "image::Bgr::from_slice_mut", + "image::Bgra::from_slice_mut", + "image::Luma::from_slice_mut", + "image::LumaA::from_slice_mut", + "image::Rgb::from_slice_mut", + "image::Rgba::from_slice_mut" + ], + "id": "RUSTSEC-2020-0073", + "ranges": [ + { + "introduced": "0.0.0-0" + }, + { + "fixed": "0.23.12" + } + ] + } + ], + "lettre": [ + { + "functions": [ + "lettre::sendmail::SendmailTransport::send", + "lettre::transport::sendmail::SendmailTransport::send", + "lettre::transport::sendmail::SendmailTransport::send_raw" + ], + "id": "RUSTSEC-2020-0069", + "ranges": [ + { + "introduced": "0.7.0" + }, + { + "fixed": "0.7.1" + }, + { + "introduced": "0.8.0" + }, + { + "fixed": "0.8.4" + }, + { + "introduced": "0.9.0" + }, + { + "fixed": "0.9.5" + }, + { + "introduced": "0.10.0-alpha.1" + }, + { + "fixed": "0.10.0-alpha.4" + } + ] + }, + { + "functions": [ + "lettre::smtp::SmtpTransport::send", + "lettre::transport::smtp::SmtpTransport::send", + "lettre::transport::smtp::SmtpTransport::send_raw" + ], + "id": "RUSTSEC-2021-0069", + "ranges": [ + { + "introduced": "0.7.0" + }, + { + "fixed": "0.9.6" + }, + { + "introduced": "0.10.0-alpha.1" + }, + { + "fixed": "0.10.0-rc.3" + } + ] + } + ], + "lock_api": [ + { + "functions": [ + "lock_api::MappedMutexGuard", + "lock_api::MappedRwLockReadGuard", + "lock_api::MappedRwLockWriteGuard", + "lock_api::RwLockReadGuard", + "lock_api::RwLockWriteGuard" + ], + "id": "RUSTSEC-2020-0070", + "ranges": [ + { + "introduced": "0.0.0-0" + }, + { + "fixed": "0.4.2" + } + ] + } + ], + "openssl": [ + { + "functions": [ + "openssl::x509::X509NameBuilder::build" + ], + "id": "RUSTSEC-2023-0022", + "ranges": [ + { + "introduced": "0.0.0-0" + }, + { + "fixed": "0.10.48" + } + ] + }, + { + "functions": [ + "openssl::x509::extension::ExtendedKeyUsage::other", + "openssl::x509::extension::SubjectAlternativeName::new" + ], + "id": "RUSTSEC-2023-0023", + "ranges": [ + { + "introduced": "0.0.0-0" + }, + { + "fixed": "0.10.48" + } + ] + }, + { + "functions": [ + "openssl::x509::X509Extension::new", + "openssl::x509::X509Extension::new_nid" + ], + "id": "RUSTSEC-2023-0024", + "ranges": [ + { + "introduced": "0.0.0-0" + }, + { + "fixed": "0.10.48" + } + ] + }, + { + "functions": [ + "openssl::x509::verify::X509VerifyParamRef::set_host" + ], + "id": "RUSTSEC-2023-0044", + "ranges": [ + { + "introduced": "0.0.0-0" + }, + { + "fixed": "0.10.55" + } + ] + }, + { + "functions": [ + "openssl::x509::store::X509StoreRef::objects" + ], + "id": "RUSTSEC-2023-0072", + "ranges": [ + { + "introduced": "0.0.0-0" + }, + { + "fixed": "0.10.60" + } + ] + }, + { + "functions": [ + "openssl::bio::MemBio::get_buf" + ], + "id": "RUSTSEC-2024-0357", + "ranges": [ + { + "introduced": "0.0.0-0" + }, + { + "fixed": "0.10.66" + } + ] + }, + { + "functions": [ + "openssl::ssl::select_next_proto" + ], + "id": "RUSTSEC-2025-0004", + "ranges": [ + { + "introduced": "0.0.0-0" + }, + { + "fixed": "0.10.70" + } + ] + }, + { + "functions": [ + "openssl::cipher::Cipher::fetch", + "openssl::md::Md::fetch" + ], + "id": "RUSTSEC-2025-0022", + "ranges": [ + { + "introduced": "0.10.39" + }, + { + "fixed": "0.10.72" + } + ] + } + ], + "postgres-protocol": [ + { + "functions": [ + "postgres_protocol::authentication::sasl::ScramSha256::update" + ], + "id": "RUSTSEC-2026-0179", + "ranges": [ + { + "introduced": "0.3.0" + }, + { + "fixed": "0.6.12" + } + ] + }, + { + "functions": [ + "postgres_protocol::types::hstore_from_sql" + ], + "id": "RUSTSEC-2026-0180", + "ranges": [ + { + "introduced": "0.0.0-0" + }, + { + "fixed": "0.6.12" + } + ] + } + ], + "rand": [ + { + "functions": [ + "rand::rng", + "rand::thread_rng" + ], + "id": "RUSTSEC-2026-0097", + "ranges": [ + { + "introduced": "0.7.0" + }, + { + "fixed": "0.8.6" + }, + { + "introduced": "0.9.0" + }, + { + "fixed": "0.9.3" + }, + { + "introduced": "0.10.0" + }, + { + "fixed": "0.10.1" + } + ] + } + ], + "rulinalg": [ + { + "functions": [ + "rulinalg::matrix::RowMut::raw_slice", + "rulinalg::matrix::RowMut::raw_slice_mut" + ], + "id": "RUSTSEC-2020-0023", + "ranges": [ + { + "introduced": "0.4.0" + } + ] + } + ], + "rusqlite": [ + { + "functions": [ + "rusqlite::Connection::get_aux", + "rusqlite::Connection::set_aux", + "rusqlite::session::Session::attach", + "rusqlite::session::Session::diff", + "rusqlite::trace::log", + "rusqlite::vtab::create_module" + ], + "id": "RUSTSEC-2020-0014", + "ranges": [ + { + "introduced": "0.0.0-0" + }, + { + "fixed": "0.23.0" + } + ] + }, + { + "functions": [ + "rusqlite::Connection::commit_hook", + "rusqlite::Connection::create_aggregate_function", + "rusqlite::Connection::create_collation", + "rusqlite::Connection::create_scalar_function", + "rusqlite::Connection::create_window_function", + "rusqlite::Connection::rollback_hook", + "rusqlite::Connection::update_hook" + ], + "id": "RUSTSEC-2021-0128", + "ranges": [ + { + "introduced": "0.25.0" + }, + { + "fixed": "0.25.4" + }, + { + "introduced": "0.26.0-0" + }, + { + "fixed": "0.26.2" + } + ] + } + ], + "smallvec": [ + { + "functions": [ + "smallvec::SmallVec::grow" + ], + "id": "RUSTSEC-2019-0009", + "ranges": [ + { + "introduced": "0.6.5" + }, + { + "fixed": "0.6.10" + } + ] + }, + { + "functions": [ + "smallvec::SmallVec::grow" + ], + "id": "RUSTSEC-2019-0012", + "ranges": [ + { + "introduced": "0.6.3" + }, + { + "fixed": "0.6.10" + } + ] + }, + { + "functions": [ + "smallvec::SmallVec::insert_many" + ], + "id": "RUSTSEC-2021-0003", + "ranges": [ + { + "introduced": "0.6.3" + }, + { + "fixed": "0.6.14" + }, + { + "introduced": "1.0.0" + }, + { + "fixed": "1.6.1" + } + ] + } + ], + "time": [ + { + "functions": [ + "time::OffsetDateTime::now_local", + "time::OffsetDateTime::try_now_local", + "time::UtcOffset::current_local_offset", + "time::UtcOffset::local_offset_at", + "time::UtcOffset::try_current_local_offset", + "time::UtcOffset::try_local_offset_at", + "time::at", + "time::at_utc", + "time::now" + ], + "id": "RUSTSEC-2020-0071", + "ranges": [ + { + "introduced": "0.0.0-0" + }, + { + "fixed": "0.2.0" + }, + { + "introduced": "0.2.1-0" + }, + { + "fixed": "0.2.1" + }, + { + "introduced": "0.2.2-0" + }, + { + "fixed": "0.2.2" + }, + { + "introduced": "0.2.3-0" + }, + { + "fixed": "0.2.3" + }, + { + "introduced": "0.2.4-0" + }, + { + "fixed": "0.2.4" + }, + { + "introduced": "0.2.5-0" + }, + { + "fixed": "0.2.5" + }, + { + "introduced": "0.2.6-0" + }, + { + "fixed": "0.2.6" + }, + { + "introduced": "0.2.7-0" + }, + { + "fixed": "0.2.23" + } + ] + }, + { + "functions": [ + "time::Date::parse", + "time::OffsetDateTime::parse", + "time::PrimitiveDateTime::parse", + "time::Time::parse", + "time::UtcDateTime::parse", + "time::UtcOffset::parse", + "time::parsing::Parsed::parse_item" + ], + "id": "RUSTSEC-2026-0009", + "ranges": [ + { + "introduced": "0.3.6" + }, + { + "fixed": "0.3.47" + } + ] + } + ], + "tokio": [ + { + "functions": [ + "tokio::task::JoinHandle::abort" + ], + "id": "RUSTSEC-2021-0072", + "ranges": [ + { + "introduced": "0.3.0" + }, + { + "fixed": "1.5.1" + }, + { + "introduced": "1.6.0" + }, + { + "fixed": "1.6.3" + }, + { + "introduced": "1.7.0" + }, + { + "fixed": "1.7.2" + }, + { + "introduced": "1.8.0" + }, + { + "fixed": "1.8.1" + } + ] + }, + { + "functions": [ + "tokio::sync::oneshot::Receiver::close" + ], + "id": "RUSTSEC-2021-0124", + "ranges": [ + { + "introduced": "0.1.14" + }, + { + "fixed": "1.8.4" + }, + { + "introduced": "1.9.0" + }, + { + "fixed": "1.13.1" + } + ] + } + ], + "tokio-postgres": [ + { + "functions": [ + "tokio_postgres::Row::get", + "tokio_postgres::Row::try_get", + "tokio_postgres::SimpleQueryRow::get", + "tokio_postgres::SimpleQueryRow::try_get" + ], + "id": "RUSTSEC-2026-0178", + "ranges": [ + { + "introduced": "0.4.0" + }, + { + "fixed": "0.7.18" + } + ] + } + ] + }, + "go": { + "github.com/antchfx/xmlquery": [ + { + "id": "GO-2020-0048", + "imports": [ + { + "path": "github.com/antchfx/xmlquery", + "symbols": [ + "LoadURL" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "1.3.1" + } + ] + } + ], + "github.com/antchfx/xpath": [ + { + "id": "GO-2026-4526", + "imports": [ + { + "path": "github.com/antchfx/xpath", + "symbols": [ + "Expr.Evaluate", + "NodeIterator.MoveNext", + "ancestorQuery.Evaluate", + "ancestorQuery.Select", + "attributeQuery.Evaluate", + "attributeQuery.Select", + "booleanQuery.Evaluate", + "booleanQuery.Select", + "cachedChildQuery.Evaluate", + "cachedChildQuery.Select", + "childQuery.Evaluate", + "childQuery.Select", + "descendantOverDescendantQuery.Evaluate", + "descendantOverDescendantQuery.Select", + "descendantQuery.Evaluate", + "descendantQuery.Select", + "filterQuery.Evaluate", + "filterQuery.Select", + "followingQuery.Evaluate", + "followingQuery.Select", + "functionQuery.Evaluate", + "groupQuery.Evaluate", + "groupQuery.Select", + "lastFuncQuery.Evaluate", + "logicalQuery.Evaluate", + "logicalQuery.Select", + "mergeQuery.Evaluate", + "mergeQuery.Select", + "numericQuery.Evaluate", + "parentQuery.Evaluate", + "parentQuery.Select", + "precedingQuery.Evaluate", + "precedingQuery.Select", + "selfQuery.Evaluate", + "selfQuery.Select", + "transformFunctionQuery.Evaluate", + "transformFunctionQuery.Select", + "unionQuery.Evaluate", + "unionQuery.Select" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "1.3.6" + } + ] + } + ], + "github.com/gorilla/websocket": [ + { + "id": "GO-2020-0019", + "imports": [ + { + "path": "github.com/gorilla/websocket", + "symbols": [ + "Conn.Close", + "Conn.NextReader", + "Conn.NextWriter", + "Conn.ReadJSON", + "Conn.ReadMessage", + "Conn.WriteControl", + "Conn.WriteJSON", + "Conn.WriteMessage", + "Conn.WritePreparedMessage", + "Conn.advanceFrame", + "Dialer.Dial", + "Dialer.DialContext", + "NewClient", + "NewPreparedMessage", + "ReadJSON", + "Subprotocols", + "Upgrade", + "Upgrader.Upgrade", + "WriteJSON", + "flateReadWrapper.Read", + "flateWriteWrapper.Close", + "flateWriteWrapper.Write", + "httpProxyDialer.Dial", + "messageReader.Read", + "messageWriter.Close", + "messageWriter.ReadFrom", + "messageWriter.Write", + "messageWriter.WriteString", + "netDialerFunc.Dial", + "proxy_direct.Dial", + "proxy_envOnce.Get", + "proxy_socks5.Dial", + "truncWriter.Write" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "1.4.1" + } + ] + }, + { + "id": "GO-2026-6278", + "imports": [ + { + "path": "github.com/gorilla/websocket", + "symbols": [ + "Conn.Close", + "Conn.NextReader", + "Conn.NextWriter", + "Conn.ReadJSON", + "Conn.ReadMessage", + "Conn.WriteControl", + "Conn.WriteJSON", + "Conn.WriteMessage", + "Conn.WritePreparedMessage", + "Dialer.Dial", + "Dialer.DialContext", + "NewClient", + "NewPreparedMessage", + "ReadJSON", + "Subprotocols", + "Upgrade", + "Upgrader.Upgrade", + "WriteJSON", + "flateReadWrapper.Read", + "flateWriteWrapper.Close", + "flateWriteWrapper.Write", + "httpProxyDialer.Dial", + "joinReader.Read", + "messageReader.Read", + "messageWriter.Close", + "messageWriter.ReadFrom", + "messageWriter.Write", + "messageWriter.WriteString", + "netDialerFunc.Dial", + "newMaskKey", + "proxy_direct.Dial", + "proxy_envOnce.Get", + "proxy_socks5.Dial", + "truncWriter.Write" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "1.5.3" + } + ] + } + ], + "github.com/jackc/pgx/v5": [ + { + "id": "GO-2024-2567", + "imports": [ + { + "path": "github.com/jackc/pgx/v5/pgconn", + "symbols": [ + "Pipeline.Sync" + ] + } + ], + "ranges": [ + { + "introduced": "5.0.0-alpha.5" + }, + { + "fixed": "5.5.2" + } + ] + }, + { + "id": "GO-2024-2606", + "imports": [ + { + "path": "github.com/jackc/pgx/v5/internal/sanitize", + "symbols": [ + "Query.Sanitize", + "SanitizeSQL" + ] + }, + { + "path": "github.com/jackc/pgx/v5/pgproto3", + "symbols": [ + "AuthenticationCleartextPassword.Encode", + "AuthenticationGSS.Encode", + "AuthenticationGSSContinue.Encode", + "AuthenticationMD5Password.Encode", + "AuthenticationOk.Encode", + "AuthenticationSASL.Encode", + "AuthenticationSASLContinue.Encode", + "AuthenticationSASLFinal.Encode", + "Backend.Flush", + "Backend.Send", + "BackendKeyData.Encode", + "Bind.Encode", + "BindComplete.Encode", + "CancelRequest.Encode", + "Close.Encode", + "CloseComplete.Encode", + "CommandComplete.Encode", + "CopyBothResponse.Encode", + "CopyData.Encode", + "CopyDone.Encode", + "CopyFail.Encode", + "CopyInResponse.Encode", + "CopyOutResponse.Encode", + "DataRow.Encode", + "Describe.Encode", + "EmptyQueryResponse.Encode", + "ErrorResponse.Encode", + "ErrorResponse.marshalBinary", + "Execute.Encode", + "Flush.Encode", + "Frontend.Flush", + "Frontend.Send", + "Frontend.SendBind", + "Frontend.SendClose", + "Frontend.SendDescribe", + "Frontend.SendExecute", + "Frontend.SendParse", + "Frontend.SendQuery", + "Frontend.SendSync", + "Frontend.SendUnbufferedEncodedCopyData", + "FunctionCall.Encode", + "FunctionCallResponse.Encode", + "GSSEncRequest.Encode", + "GSSResponse.Encode", + "NoData.Encode", + "NoticeResponse.Encode", + "NotificationResponse.Encode", + "ParameterDescription.Encode", + "ParameterStatus.Encode", + "Parse.Encode", + "ParseComplete.Encode", + "PasswordMessage.Encode", + "PortalSuspended.Encode", + "Query.Encode", + "ReadyForQuery.Encode", + "RowDescription.Encode", + "SASLInitialResponse.Encode", + "SASLResponse.Encode", + "SSLRequest.Encode", + "StartupMessage.Encode", + "Sync.Encode", + "Terminate.Encode" + ] + }, + { + "path": "github.com/jackc/pgx/v5/pgconn", + "symbols": [ + "Batch.ExecParams", + "Batch.ExecPrepared", + "Connect", + "ConnectConfig", + "ConnectWithOptions", + "MultiResultReader.Close", + "MultiResultReader.NextResult", + "MultiResultReader.ReadAll", + "PgConn.CheckConn", + "PgConn.Close", + "PgConn.CopyFrom", + "PgConn.CopyTo", + "PgConn.Deallocate", + "PgConn.Exec", + "PgConn.ExecBatch", + "PgConn.ExecParams", + "PgConn.ExecPrepared", + "PgConn.Ping", + "PgConn.Prepare", + "PgConn.ReceiveMessage", + "PgConn.SyncConn", + "PgConn.WaitForNotification", + "Pipeline.Close", + "Pipeline.Flush", + "Pipeline.GetResults", + "Pipeline.SendDeallocate", + "Pipeline.SendPrepare", + "Pipeline.SendQueryParams", + "Pipeline.SendQueryPrepared", + "Pipeline.Sync", + "ResultReader.Close", + "ResultReader.NextRow", + "ResultReader.Read", + "ValidateConnectTargetSessionAttrsPreferStandby", + "ValidateConnectTargetSessionAttrsPrimary", + "ValidateConnectTargetSessionAttrsReadOnly", + "ValidateConnectTargetSessionAttrsReadWrite", + "ValidateConnectTargetSessionAttrsStandby" + ] + }, + { + "path": "github.com/jackc/pgx/v5/pgproto3/example/pgfortune", + "symbols": [ + "PgFortuneBackend.Run", + "PgFortuneBackend.handleStartup", + "main" + ] + } + ], + "ranges": [ + { + "introduced": "5.0.0" + }, + { + "fixed": "5.5.4" + } + ] + }, + { + "id": "GO-2026-4771", + "imports": [ + { + "path": "github.com/jackc/pgx/v5/pgproto3", + "symbols": [ + "Backend.Receive", + "Bind.Decode" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "5.9.0" + } + ] + }, + { + "id": "GO-2026-4772", + "imports": [ + { + "path": "github.com/jackc/pgx/v5/pgproto3", + "symbols": [ + "Backend.Receive", + "FunctionCall.Decode" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "5.9.0" + } + ] + }, + { + "id": "GO-2026-5004", + "imports": [ + { + "path": "github.com/jackc/pgx/v5/internal/sanitize", + "symbols": [ + "Query.Sanitize", + "SanitizeSQL" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "5.9.2" + } + ] + } + ], + "github.com/rabbitmq/amqp091-go": [ + { + "id": "GO-2026-6372", + "imports": [], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "1.13.0" + } + ] + }, + { + "id": "GO-2026-6491", + "imports": [ + { + "path": "github.com/rabbitmq/amqp091-go", + "symbols": [ + "Connection.Reconnect", + "Connection.openComplete", + "DefaultConnectionRecovery.OnConnectionClose", + "Dial", + "DialConfig", + "DialTLS", + "DialTLS_ExternalAuth", + "Open" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "1.13.0" + } + ] + }, + { + "id": "GO-2026-6492", + "imports": [ + { + "path": "github.com/rabbitmq/amqp091-go", + "symbols": [ + "Dial", + "DialConfig", + "DialTLS", + "DialTLS_ExternalAuth", + "tlsConfigFromURI" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "1.13.0" + } + ] + }, + { + "id": "GO-2026-6493", + "imports": [ + { + "path": "github.com/rabbitmq/amqp091-go", + "symbols": [ + "URI.String" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "1.13.0" + } + ] + }, + { + "id": "GO-2026-6494", + "imports": [ + { + "path": "github.com/rabbitmq/amqp091-go", + "symbols": [ + "Connection.Reconnect", + "DefaultConnectionRecovery.OnConnectionClose", + "Dial", + "DialConfig", + "DialTLS", + "DialTLS_ExternalAuth", + "Open", + "readField", + "reader.ReadFrame" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "1.13.0" + } + ] + }, + { + "id": "GO-2026-6496", + "imports": [ + { + "path": "github.com/rabbitmq/amqp091-go", + "symbols": [ + "Connection.Reconnect", + "DefaultConnectionRecovery.OnConnectionClose", + "Dial", + "DialConfig", + "DialTLS", + "DialTLS_ExternalAuth", + "Open", + "readLongstr", + "reader.ReadFrame" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "1.13.0" + } + ] + }, + { + "id": "GO-2026-6498", + "imports": [ + { + "path": "github.com/rabbitmq/amqp091-go", + "symbols": [ + "AMQPlainAuth.Response", + "Channel.Ack", + "Channel.Cancel", + "Channel.Close", + "Channel.Confirm", + "Channel.Consume", + "Channel.ConsumeWithContext", + "Channel.ExchangeBind", + "Channel.ExchangeDeclare", + "Channel.ExchangeDeclarePassive", + "Channel.ExchangeDelete", + "Channel.ExchangeUnbind", + "Channel.Flow", + "Channel.Get", + "Channel.Nack", + "Channel.Publish", + "Channel.PublishWithContext", + "Channel.PublishWithDeferredConfirm", + "Channel.PublishWithDeferredConfirmWithContext", + "Channel.Qos", + "Channel.QueueBind", + "Channel.QueueDeclare", + "Channel.QueueDeclarePassive", + "Channel.QueueDelete", + "Channel.QueueInspect", + "Channel.QueuePurge", + "Channel.QueueUnbind", + "Channel.Reconnect", + "Channel.Recover", + "Channel.Reject", + "Channel.Tx", + "Channel.TxCommit", + "Channel.TxRollback", + "Connection.Channel", + "Connection.Close", + "Connection.CloseDeadline", + "Connection.Reconnect", + "Connection.UpdateSecret", + "DefaultConnectionRecovery.OnChannelClose", + "DefaultConnectionRecovery.OnConnectionClose", + "Delivery.Ack", + "Delivery.Nack", + "Delivery.Reject", + "Dial", + "DialConfig", + "DialTLS", + "DialTLS_ExternalAuth", + "Open", + "writeShortstr", + "writer.WriteFrame", + "writer.WriteFrameNoFlush" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "1.13.0" + } + ] + }, + { + "id": "GO-2026-6500", + "imports": [ + { + "path": "github.com/rabbitmq/amqp091-go", + "symbols": [ + "Channel.recvContent", + "Connection.Reconnect", + "DefaultConnectionRecovery.OnConnectionClose", + "Dial", + "DialConfig", + "DialTLS", + "DialTLS_ExternalAuth", + "Open" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "1.13.0" + } + ] + }, + { + "id": "GO-2026-6502", + "imports": [ + { + "path": "github.com/rabbitmq/amqp091-go", + "symbols": [ + "Channel.Qos" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "1.13.0" + } + ] + }, + { + "id": "GO-2026-6503", + "imports": [ + { + "path": "github.com/rabbitmq/amqp091-go", + "symbols": [ + "Connection.Reconnect", + "Connection.openTune", + "DefaultConnectionRecovery.OnConnectionClose", + "Dial", + "DialConfig", + "DialTLS", + "DialTLS_ExternalAuth", + "Open" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "1.13.0" + } + ] + } + ], + "github.com/sirupsen/logrus": [ + { + "id": "GO-2025-4188", + "imports": [ + { + "path": "github.com/sirupsen/logrus", + "symbols": [ + "Entry.Writer", + "Entry.WriterLevel", + "Entry.writerScanner", + "Logger.Writer", + "Logger.WriterLevel" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "1.8.3" + }, + { + "introduced": "1.9.0" + }, + { + "fixed": "1.9.1" + }, + { + "introduced": "1.9.2" + }, + { + "fixed": "1.9.3" + } + ] + } + ], + "github.com/valyala/fasthttp": [ + { + "id": "GO-2022-0355", + "imports": [ + { + "path": "github.com/valyala/fasthttp", + "symbols": [ + "AppendBrotliBytes", + "AppendBrotliBytesLevel", + "AppendDeflateBytes", + "AppendDeflateBytesLevel", + "AppendGunzipBytes", + "AppendGzipBytes", + "AppendGzipBytesLevel", + "AppendHTTPDate", + "AppendInflateBytes", + "AppendUnbrotliBytes", + "Args.WriteTo", + "Client.CloseIdleConnections", + "Client.Do", + "Client.DoDeadline", + "Client.DoRedirects", + "Client.DoTimeout", + "Client.Get", + "Client.GetDeadline", + "Client.GetTimeout", + "Client.Post", + "Cookie.AppendBytes", + "Cookie.Cookie", + "Cookie.Parse", + "Cookie.ParseBytes", + "Cookie.String", + "Cookie.WriteTo", + "Dial", + "DialDualStack", + "DialDualStackTimeout", + "DialTimeout", + "Do", + "DoDeadline", + "DoRedirects", + "DoTimeout", + "FS.NewRequestHandler", + "FSHandler", + "FileLastModified", + "GenerateTestCertificate", + "Get", + "GetDeadline", + "GetTimeout", + "HostClient.CloseIdleConnections", + "HostClient.Do", + "HostClient.DoDeadline", + "HostClient.DoRedirects", + "HostClient.DoTimeout", + "HostClient.Get", + "HostClient.GetDeadline", + "HostClient.GetTimeout", + "HostClient.Post", + "LBClient.Do", + "LBClient.DoDeadline", + "LBClient.DoTimeout", + "ListenAndServe", + "ListenAndServeTLS", + "ListenAndServeTLSEmbed", + "ListenAndServeUNIX", + "NewStreamReader", + "ParseByteRange", + "ParseHTTPDate", + "ParseIPv4", + "PipelineClient.Do", + "PipelineClient.DoDeadline", + "PipelineClient.DoTimeout", + "PipelineClient.PendingRequests", + "Post", + "Request.Body", + "Request.BodyGunzip", + "Request.BodyInflate", + "Request.BodyUnbrotli", + "Request.BodyWriteTo", + "Request.ContinueReadBody", + "Request.ContinueReadBodyStream", + "Request.Host", + "Request.MultipartForm", + "Request.PostArgs", + "Request.Read", + "Request.ReadBody", + "Request.ReadLimitBody", + "Request.SetBodyStreamWriter", + "Request.SetHost", + "Request.SetHostBytes", + "Request.String", + "Request.SwapBody", + "Request.URI", + "Request.Write", + "Request.WriteTo", + "RequestCtx.FormFile", + "RequestCtx.FormValue", + "RequestCtx.Host", + "RequestCtx.IfModifiedSince", + "RequestCtx.MultipartForm", + "RequestCtx.Path", + "RequestCtx.PostArgs", + "RequestCtx.PostBody", + "RequestCtx.QueryArgs", + "RequestCtx.Redirect", + "RequestCtx.RedirectBytes", + "RequestCtx.SendFile", + "RequestCtx.SendFileBytes", + "RequestCtx.SetBodyStreamWriter", + "RequestCtx.String", + "RequestCtx.URI", + "RequestHeader.Add", + "RequestHeader.AddBytesK", + "RequestHeader.AddBytesKV", + "RequestHeader.AddBytesV", + "RequestHeader.Read", + "RequestHeader.ReadTrailer", + "RequestHeader.Set", + "RequestHeader.SetByteRange", + "RequestHeader.SetBytesK", + "RequestHeader.SetBytesKV", + "RequestHeader.SetBytesV", + "RequestHeader.SetCanonical", + "RequestHeader.SetReferer", + "RequestHeader.SetRefererBytes", + "RequestHeader.Write", + "Response.Body", + "Response.BodyGunzip", + "Response.BodyInflate", + "Response.BodyUnbrotli", + "Response.BodyWriteTo", + "Response.Read", + "Response.ReadBody", + "Response.ReadLimitBody", + "Response.SendFile", + "Response.SetBodyStreamWriter", + "Response.String", + "Response.SwapBody", + "Response.Write", + "Response.WriteDeflate", + "Response.WriteDeflateLevel", + "Response.WriteGzip", + "Response.WriteGzipLevel", + "Response.WriteTo", + "ResponseHeader.Add", + "ResponseHeader.AddBytesK", + "ResponseHeader.AddBytesKV", + "ResponseHeader.AddBytesV", + "ResponseHeader.AppendBytes", + "ResponseHeader.Cookie", + "ResponseHeader.DelClientCookie", + "ResponseHeader.DelClientCookieBytes", + "ResponseHeader.Header", + "ResponseHeader.Read", + "ResponseHeader.ReadTrailer", + "ResponseHeader.Set", + "ResponseHeader.SetBytesK", + "ResponseHeader.SetBytesKV", + "ResponseHeader.SetBytesV", + "ResponseHeader.SetCanonical", + "ResponseHeader.SetContentRange", + "ResponseHeader.SetCookie", + "ResponseHeader.SetLastModified", + "ResponseHeader.String", + "ResponseHeader.Write", + "ResponseHeader.WriteTo", + "SaveMultipartFile", + "Serve", + "ServeConn", + "ServeFile", + "ServeFileBytes", + "ServeFileBytesUncompressed", + "ServeFileUncompressed", + "ServeTLS", + "ServeTLSEmbed", + "Server.AppendCert", + "Server.AppendCertEmbed", + "Server.ListenAndServe", + "Server.ListenAndServeTLS", + "Server.ListenAndServeTLSEmbed", + "Server.ListenAndServeUNIX", + "Server.Serve", + "Server.ServeConn", + "Server.ServeTLS", + "Server.ServeTLSEmbed", + "Server.Shutdown", + "TCPDialer.Dial", + "TCPDialer.DialDualStack", + "TCPDialer.DialDualStackTimeout", + "TCPDialer.DialTimeout", + "URI.Parse", + "URI.Update", + "URI.UpdateBytes", + "URI.WriteTo", + "WriteBrotli", + "WriteBrotliLevel", + "WriteDeflate", + "WriteDeflateLevel", + "WriteGunzip", + "WriteGzip", + "WriteGzipLevel", + "WriteInflate", + "WriteMultipartForm", + "WriteUnbrotli", + "bigFileReader.Read", + "bigFileReader.WriteTo", + "ctxLogger.Printf", + "firstByteReader.Read", + "flushWriter.Write", + "fsFile.NewReader", + "fsSmallFileReader.WriteTo", + "hijackConn.Close", + "hijackConn.Read", + "perIPConn.Close", + "perIPConnCounter.Unregister", + "pipelineConnClient.Do", + "pipelineConnClient.DoDeadline", + "pipelineConnClient.PendingRequests", + "requestStream.Read", + "statsWriter.Write", + "tcpKeepaliveListener.Accept", + "workerPool.Serve" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "1.34.0" + } + ] + }, + { + "id": "GO-2026-4950", + "imports": [ + { + "path": "github.com/valyala/fasthttp", + "symbols": [ + "RequestCtx.SendFile", + "RequestCtx.SendFileBytes", + "ServeFS", + "ServeFile", + "ServeFileBytes", + "ServeFileBytesUncompressed", + "ServeFileUncompressed" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "1.70.0" + } + ] + } + ], + "golang.org/x/crypto": [ + { + "id": "GO-2020-0012", + "imports": [ + { + "path": "golang.org/x/crypto/ssh", + "symbols": [ + "CertChecker.Authenticate", + "CertChecker.CheckCert", + "CertChecker.CheckHostKey", + "Certificate.Verify", + "Dial", + "NewClientConn", + "NewPublicKey", + "NewServerConn", + "NewSignerFromKey", + "NewSignerFromSigner", + "ParseAuthorizedKey", + "ParseKnownHosts", + "ParsePrivateKey", + "ParsePrivateKeyWithPassphrase", + "ParsePublicKey", + "ParseRawPrivateKey", + "ParseRawPrivateKeyWithPassphrase", + "ed25519PublicKey.Verify", + "parseED25519", + "parseSKEd25519", + "skEd25519PublicKey.Verify" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.0.0-20200220183623-bac4c82f6975" + } + ] + }, + { + "id": "GO-2020-0013", + "imports": [ + { + "path": "golang.org/x/crypto/ssh", + "symbols": [ + "Dial", + "NewClientConn" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.0.0-20170330155735-e4e2799dd7aa" + } + ] + }, + { + "id": "GO-2021-0227", + "imports": [ + { + "path": "golang.org/x/crypto/ssh", + "symbols": [ + "NewServerConn", + "connection.serverAuthenticate" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.0.0-20201216223049-8b5274cf687f" + } + ] + }, + { + "id": "GO-2021-0356", + "imports": [ + { + "path": "golang.org/x/crypto/ssh", + "symbols": [ + "ServerConfig.AddHostKey" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.0.0-20220314234659-1baeb1ce4c0b" + } + ] + }, + { + "id": "GO-2022-0209", + "imports": [ + { + "path": "golang.org/x/crypto/salsa20/salsa", + "symbols": [ + "XORKeyStream" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.0.0-20190320223903-b7391e95e576" + } + ] + }, + { + "id": "GO-2022-0229", + "imports": [ + { + "path": "golang.org/x/crypto/cryptobyte", + "symbols": [] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.0.0-20200124225646-8b5121be2f68" + } + ] + }, + { + "id": "GO-2022-0968", + "imports": [ + { + "path": "golang.org/x/crypto/ssh", + "symbols": [ + "Dial", + "NewClientConn", + "NewServerConn", + "chacha20Poly1305Cipher.readCipherPacket", + "curve25519sha256.Client", + "curve25519sha256.Server", + "dhGEXSHA.Client", + "dhGEXSHA.Server", + "dhGroup.Client", + "dhGroup.Server", + "ecdh.Client", + "ecdh.Server", + "gcmCipher.readCipherPacket" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.0.0-20211202192323-5770296d904e" + } + ] + }, + { + "id": "GO-2023-1992", + "imports": [ + { + "path": "golang.org/x/crypto/openpgp/clearsign", + "symbols": [ + "Decode" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.0.0-20190424203555-c05e17bb3b2d" + } + ] + }, + { + "id": "GO-2023-2402", + "imports": [ + { + "path": "golang.org/x/crypto/ssh", + "symbols": [ + "Client.Dial", + "Client.DialContext", + "Client.DialTCP", + "Client.Listen", + "Client.ListenTCP", + "Client.ListenUnix", + "Client.NewSession", + "Dial", + "DiscardRequests", + "NewClient", + "NewClientConn", + "NewServerConn", + "Request.Reply", + "Session.Close", + "Session.CombinedOutput", + "Session.Output", + "Session.RequestPty", + "Session.RequestSubsystem", + "Session.Run", + "Session.SendRequest", + "Session.Setenv", + "Session.Shell", + "Session.Signal", + "Session.Start", + "Session.WindowChange", + "channel.Accept", + "channel.Close", + "channel.CloseWrite", + "channel.Read", + "channel.ReadExtended", + "channel.Reject", + "channel.SendRequest", + "channel.Write", + "channel.WriteExtended", + "connectionState.readPacket", + "connectionState.writePacket", + "curve25519sha256.Client", + "curve25519sha256.Server", + "dhGEXSHA.Client", + "dhGEXSHA.Server", + "dhGroup.Client", + "dhGroup.Server", + "ecdh.Client", + "ecdh.Server", + "extChannel.Read", + "extChannel.Write", + "handshakeTransport.enterKeyExchange", + "handshakeTransport.readLoop", + "handshakeTransport.sendKexInit", + "mux.OpenChannel", + "mux.SendRequest", + "sessionStdin.Close", + "sshClientKeyboardInteractive.Challenge", + "tcpListener.Accept", + "tcpListener.Close", + "transport.readPacket", + "transport.writePacket", + "unixListener.Accept", + "unixListener.Close" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.17.0" + } + ] + }, + { + "id": "GO-2024-2961", + "imports": [ + { + "path": "golang.org/x/crypto/acme/autocert", + "symbols": [ + "DirCache.Delete", + "DirCache.Get", + "DirCache.Put", + "HostWhitelist", + "Manager.GetCertificate", + "Manager.Listener", + "NewListener", + "listener.Accept", + "listener.Close" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.0.0-20220525230936-793ad666bf5e" + } + ] + }, + { + "id": "GO-2024-3321", + "imports": [ + { + "path": "golang.org/x/crypto/ssh", + "symbols": [ + "NewServerConn", + "ServerConfig.PublicKeyCallback", + "connection.serverAuthenticate" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.31.0" + } + ] + }, + { + "id": "GO-2025-3487", + "imports": [ + { + "path": "golang.org/x/crypto/ssh", + "symbols": [ + "Client.Dial", + "Client.DialContext", + "Client.DialTCP", + "Client.Listen", + "Client.ListenTCP", + "Client.ListenUnix", + "Client.NewSession", + "Dial", + "DiscardRequests", + "NewClient", + "NewClientConn", + "NewServerConn", + "Request.Reply", + "Session.Close", + "Session.CombinedOutput", + "Session.Output", + "Session.RequestPty", + "Session.RequestSubsystem", + "Session.Run", + "Session.SendRequest", + "Session.Setenv", + "Session.Shell", + "Session.Signal", + "Session.Start", + "Session.WindowChange", + "channel.Accept", + "channel.Close", + "channel.CloseWrite", + "channel.Read", + "channel.ReadExtended", + "channel.Reject", + "channel.SendRequest", + "channel.Write", + "channel.WriteExtended", + "connection.SendAuthBanner", + "curve25519sha256.Client", + "curve25519sha256.Server", + "dhGEXSHA.Client", + "dhGEXSHA.Server", + "dhGroup.Client", + "dhGroup.Server", + "ecdh.Client", + "ecdh.Server", + "extChannel.Read", + "extChannel.Write", + "handshakeTransport.kexLoop", + "handshakeTransport.recordWriteError", + "handshakeTransport.writePacket", + "mux.OpenChannel", + "mux.SendRequest", + "newHandshakeTransport", + "sessionStdin.Close", + "sshClientKeyboardInteractive.Challenge", + "tcpListener.Accept", + "tcpListener.Close", + "unixListener.Accept", + "unixListener.Close" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.35.0" + } + ] + }, + { + "id": "GO-2025-4116", + "imports": [ + { + "path": "golang.org/x/crypto/ssh/agent", + "symbols": [ + "agentKeyringSigner.Sign", + "agentKeyringSigner.SignWithAlgorithm", + "client.List", + "client.Sign", + "client.SignWithFlags", + "client.Signers" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.43.0" + } + ] + }, + { + "id": "GO-2025-4134", + "imports": [ + { + "path": "golang.org/x/crypto/ssh", + "symbols": [ + "NewServerConn", + "parseGSSAPIPayload" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.45.0" + } + ] + }, + { + "id": "GO-2025-4135", + "imports": [ + { + "path": "golang.org/x/crypto/ssh/agent", + "symbols": [ + "ForwardToAgent", + "ServeAgent", + "parseConstraints" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.45.0" + } + ] + }, + { + "id": "GO-2026-5005", + "imports": [ + { + "path": "golang.org/x/crypto/ssh/agent", + "symbols": [ + "keyring.Add" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.52.0" + } + ] + }, + { + "id": "GO-2026-5006", + "imports": [ + { + "path": "golang.org/x/crypto/ssh/agent", + "symbols": [ + "client.Add", + "keyring.Add" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.52.0" + } + ] + }, + { + "id": "GO-2026-5013", + "imports": [ + { + "path": "golang.org/x/crypto/ssh", + "symbols": [ + "Dial", + "NewClientConn", + "NewServerConn", + "curve25519sha256.Client", + "curve25519sha256.Server", + "dhGEXSHA.Client", + "dhGEXSHA.Server", + "dhGroup.Client", + "dhGroup.Server", + "ecdh.Client", + "ecdh.Server", + "gcmCipher.readCipherPacket", + "mlkem768WithCurve25519sha256.Client", + "mlkem768WithCurve25519sha256.Server" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.52.0" + } + ] + }, + { + "id": "GO-2026-5014", + "imports": [ + { + "path": "golang.org/x/crypto/ssh", + "symbols": [ + "NewServerConn", + "connection.serverAuthenticate" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.52.0" + } + ] + }, + { + "id": "GO-2026-5015", + "imports": [ + { + "path": "golang.org/x/crypto/ssh", + "symbols": [ + "CertChecker.Authenticate", + "CertChecker.CheckHostKey" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.52.0" + } + ] + }, + { + "id": "GO-2026-5016", + "imports": [ + { + "path": "golang.org/x/crypto/ssh", + "symbols": [ + "channel.Reject" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.52.0" + } + ] + }, + { + "id": "GO-2026-5017", + "imports": [ + { + "path": "golang.org/x/crypto/ssh", + "symbols": [ + "Client.Listen", + "Client.ListenTCP", + "Client.ListenUnix", + "Dial", + "NewClientConn", + "NewServerConn", + "Session.CombinedOutput", + "Session.Output", + "Session.RequestPty", + "Session.RequestSubsystem", + "Session.Run", + "Session.SendRequest", + "Session.Setenv", + "Session.Shell", + "Session.Signal", + "Session.Start", + "Session.WindowChange", + "channel.SendRequest", + "channel.handlePacket", + "mux.SendRequest", + "mux.handleGlobalPacket", + "tcpListener.Close", + "unixListener.Close" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.52.0" + } + ] + }, + { + "id": "GO-2026-5018", + "imports": [ + { + "path": "golang.org/x/crypto/ssh", + "symbols": [ + "Dial", + "NewClientConn", + "NewServerConn", + "NewSignerFromKey", + "ParseAuthorizedKey", + "ParseKnownHosts", + "ParsePrivateKey", + "ParsePrivateKeyWithPassphrase", + "ParsePublicKey", + "ParseRawPrivateKey", + "ParseRawPrivateKeyWithPassphrase", + "checkDSAParams", + "parseDSA", + "parseRSA" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.52.0" + } + ] + }, + { + "id": "GO-2026-5019", + "imports": [ + { + "path": "golang.org/x/crypto/ssh", + "symbols": [ + "CertChecker.Authenticate", + "CertChecker.CheckCert", + "CertChecker.CheckHostKey", + "Certificate.Verify", + "Dial", + "NewClientConn", + "NewServerConn", + "connection.serverAuthenticate", + "skECDSAPublicKey.Verify", + "skEd25519PublicKey.Verify" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.52.0" + } + ] + }, + { + "id": "GO-2026-5020", + "imports": [ + { + "path": "golang.org/x/crypto/ssh", + "symbols": [ + "Dial", + "NewClientConn", + "NewServerConn", + "Session.CombinedOutput", + "Session.Output", + "Session.Run", + "Session.Shell", + "Session.Start", + "channel.Write", + "channel.WriteExtended", + "curve25519sha256.Client", + "curve25519sha256.Server", + "dhGEXSHA.Client", + "dhGEXSHA.Server", + "dhGroup.Client", + "dhGroup.Server", + "ecdh.Client", + "ecdh.Server", + "extChannel.Write", + "mlkem768WithCurve25519sha256.Client", + "mlkem768WithCurve25519sha256.Server" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.52.0" + } + ] + }, + { + "id": "GO-2026-5021", + "imports": [ + { + "path": "golang.org/x/crypto/ssh/knownhosts", + "symbols": [ + "hostKeyDB.IsRevoked" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.52.0" + } + ] + }, + { + "id": "GO-2026-5023", + "imports": [ + { + "path": "golang.org/x/crypto/ssh", + "symbols": [ + "NewServerConn", + "connection.serverAuthenticate" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.52.0" + } + ] + }, + { + "id": "GO-2026-5033", + "imports": [ + { + "path": "golang.org/x/crypto/ssh/agent", + "symbols": [ + "ForwardToAgent", + "ServeAgent", + "parseEd25519Cert", + "parseEd25519Key" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.52.0" + } + ] + }, + { + "id": "GO-2026-5932", + "imports": [ + { + "path": "golang.org/x/crypto/openpgp", + "symbols": [] + }, + { + "path": "golang.org/x/crypto/openpgp/packet", + "symbols": [] + }, + { + "path": "golang.org/x/crypto/openpgp/armor", + "symbols": [] + }, + { + "path": "golang.org/x/crypto/openpgp/clearsign", + "symbols": [] + }, + { + "path": "golang.org/x/crypto/openpgp/errors", + "symbols": [] + }, + { + "path": "golang.org/x/crypto/openpgp/elgamal", + "symbols": [] + }, + { + "path": "golang.org/x/crypto/openpgp/s2k", + "symbols": [] + } + ], + "ranges": [ + { + "introduced": "0" + } + ] + }, + { + "id": "GO-2026-6303", + "imports": [ + { + "path": "golang.org/x/crypto/ssh", + "symbols": [ + "NewServerConn", + "connection.serverAuthenticate" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.55.0" + } + ] + }, + { + "id": "GO-2026-6354", + "imports": [ + { + "path": "golang.org/x/crypto/ssh", + "symbols": [ + "Dial", + "NewClientConn", + "NewControlClientConn", + "NewServerConn", + "channel.Accept", + "channel.handlePacket", + "channel.responseMessageReceived" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.56.0" + } + ] + }, + { + "id": "GO-2026-6355", + "imports": [ + { + "path": "golang.org/x/crypto/ssh", + "symbols": [ + "Dial", + "NewClientConn", + "NewControlClientConn", + "NewServerConn", + "channel.handlePacket" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.56.0" + } + ] + } + ], + "golang.org/x/net": [ + { + "id": "GO-2020-0014", + "imports": [ + { + "path": "golang.org/x/net/html", + "symbols": [ + "Parse", + "ParseFragment", + "inSelectIM", + "inSelectInTableIM" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.0.0-20190125091013-d26f9f9a57f3" + } + ] + }, + { + "id": "GO-2021-0078", + "imports": [ + { + "path": "golang.org/x/net/html", + "symbols": [ + "Parse", + "ParseFragment", + "inBodyIM", + "inFramesetIM" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.0.0-20180816102801-aaf60122140d" + } + ] + }, + { + "id": "GO-2021-0238", + "imports": [ + { + "path": "golang.org/x/net/html", + "symbols": [ + "Parse", + "ParseFragment", + "ParseFragmentWithOptions", + "ParseWithOptions", + "inHeadIM" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.0.0-20210520170846-37e1c6afe023" + } + ] + }, + { + "id": "GO-2022-0192", + "imports": [ + { + "path": "golang.org/x/net/html", + "symbols": [ + "Parse", + "ParseFragment", + "parser.resetInsertionMode" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.0.0-20180925071336-cf3bd585ca2a" + } + ] + }, + { + "id": "GO-2022-0193", + "imports": [ + { + "path": "golang.org/x/net/html", + "symbols": [ + "Parse", + "ParseFragment", + "inBodyIM" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.0.0-20180921000356-2f5d2388922f" + } + ] + }, + { + "id": "GO-2022-0197", + "imports": [ + { + "path": "golang.org/x/net/html", + "symbols": [ + "Parse", + "ParseFragment", + "nodeStack.contains" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.0.0-20190125002852-4b62a64f59f7" + } + ] + }, + { + "id": "GO-2022-0236", + "imports": [ + { + "path": "golang.org/x/net/http/httpguts", + "symbols": [ + "HeaderValuesContainsToken", + "headerValueContainsToken" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.0.0-20210428140749-89ef3d95e781" + } + ] + }, + { + "id": "GO-2022-0288", + "imports": [ + { + "path": "golang.org/x/net/http2", + "symbols": [ + "Server.ServeConn", + "serverConn.canonicalHeader" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.0.0-20211209124913-491a49abca63" + } + ] + }, + { + "id": "GO-2022-0536", + "imports": [ + { + "path": "golang.org/x/net/http2", + "symbols": [ + "Server.ServeConn", + "serverConn.scheduleFrameWrite", + "serverConn.serve", + "serverConn.writeFrame" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.0.0-20190813141303-74dc4d7220e7" + } + ] + }, + { + "id": "GO-2022-0969", + "imports": [ + { + "path": "golang.org/x/net/http2", + "symbols": [ + "Server.ServeConn", + "serverConn.goAway" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.0.0-20220906165146-f3363e06e74c" + } + ] + }, + { + "id": "GO-2022-1144", + "imports": [ + { + "path": "golang.org/x/net/http2", + "symbols": [ + "Server.ServeConn", + "serverConn.canonicalHeader" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.4.0" + } + ] + }, + { + "id": "GO-2023-1495", + "imports": [ + { + "path": "golang.org/x/net/http2/h2c", + "symbols": [ + "h2cHandler.ServeHTTP", + "h2cUpgrade" + ] + } + ], + "ranges": [ + { + "introduced": "0.0.0-20220524220425-1d687d428aca" + }, + { + "fixed": "0.1.1-0.20221104162952-702349b0e862" + } + ] + }, + { + "id": "GO-2023-1571", + "imports": [ + { + "path": "golang.org/x/net/http2", + "symbols": [ + "ClientConn.Close", + "ClientConn.Ping", + "ClientConn.RoundTrip", + "ClientConn.Shutdown", + "ConfigureServer", + "ConfigureTransport", + "ConfigureTransports", + "ConnectionError.Error", + "ErrCode.String", + "FrameHeader.String", + "FrameType.String", + "FrameWriteRequest.String", + "Framer.ReadFrame", + "Framer.WriteContinuation", + "Framer.WriteData", + "Framer.WriteDataPadded", + "Framer.WriteGoAway", + "Framer.WriteHeaders", + "Framer.WritePing", + "Framer.WritePriority", + "Framer.WritePushPromise", + "Framer.WriteRSTStream", + "Framer.WriteRawFrame", + "Framer.WriteSettings", + "Framer.WriteSettingsAck", + "Framer.WriteWindowUpdate", + "GoAwayError.Error", + "ReadFrameHeader", + "Server.ServeConn", + "Setting.String", + "SettingID.String", + "SettingsFrame.ForeachSetting", + "StreamError.Error", + "Transport.CloseIdleConnections", + "Transport.NewClientConn", + "Transport.RoundTrip", + "Transport.RoundTripOpt", + "bufferedWriter.Flush", + "bufferedWriter.Write", + "chunkWriter.Write", + "clientConnPool.GetClientConn", + "connError.Error", + "dataBuffer.Read", + "duplicatePseudoHeaderError.Error", + "gzipReader.Close", + "gzipReader.Read", + "headerFieldNameError.Error", + "headerFieldValueError.Error", + "noDialClientConnPool.GetClientConn", + "noDialH2RoundTripper.RoundTrip", + "pipe.Read", + "priorityWriteScheduler.CloseStream", + "priorityWriteScheduler.OpenStream", + "pseudoHeaderError.Error", + "requestBody.Close", + "requestBody.Read", + "responseWriter.Flush", + "responseWriter.FlushError", + "responseWriter.Push", + "responseWriter.SetReadDeadline", + "responseWriter.SetWriteDeadline", + "responseWriter.Write", + "responseWriter.WriteHeader", + "responseWriter.WriteString", + "serverConn.CloseConn", + "serverConn.Flush", + "stickyErrWriter.Write", + "transportResponseBody.Close", + "transportResponseBody.Read", + "writeData.String" + ] + }, + { + "path": "golang.org/x/net/http2/hpack", + "symbols": [ + "Decoder.DecodeFull", + "Decoder.Write", + "Decoder.parseFieldLiteral", + "Decoder.readString" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.7.0" + } + ] + }, + { + "id": "GO-2023-1988", + "imports": [ + { + "path": "golang.org/x/net/html", + "symbols": [ + "Render", + "render1" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.13.0" + } + ] + }, + { + "id": "GO-2023-2102", + "imports": [ + { + "path": "golang.org/x/net/http2", + "symbols": [ + "Server.ServeConn", + "serverConn.processHeaders", + "serverConn.runHandler", + "serverConn.serve", + "serverConn.upgradeRequest" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.17.0" + } + ] + }, + { + "id": "GO-2024-2687", + "imports": [ + { + "path": "golang.org/x/net/http2", + "symbols": [ + "ClientConn.Close", + "ClientConn.Ping", + "ClientConn.RoundTrip", + "ClientConn.Shutdown", + "ConfigureServer", + "ConfigureTransport", + "ConfigureTransports", + "ConnectionError.Error", + "ErrCode.String", + "FrameHeader.String", + "FrameType.String", + "FrameWriteRequest.String", + "Framer.ReadFrame", + "Framer.WriteContinuation", + "Framer.WriteData", + "Framer.WriteDataPadded", + "Framer.WriteGoAway", + "Framer.WriteHeaders", + "Framer.WritePing", + "Framer.WritePriority", + "Framer.WritePushPromise", + "Framer.WriteRSTStream", + "Framer.WriteRawFrame", + "Framer.WriteSettings", + "Framer.WriteSettingsAck", + "Framer.WriteWindowUpdate", + "Framer.readMetaFrame", + "GoAwayError.Error", + "ReadFrameHeader", + "Server.ServeConn", + "Setting.String", + "SettingID.String", + "SettingsFrame.ForeachSetting", + "StreamError.Error", + "Transport.CloseIdleConnections", + "Transport.NewClientConn", + "Transport.RoundTrip", + "Transport.RoundTripOpt", + "bufferedWriter.Flush", + "bufferedWriter.Write", + "chunkWriter.Write", + "clientConnPool.GetClientConn", + "connError.Error", + "dataBuffer.Read", + "duplicatePseudoHeaderError.Error", + "gzipReader.Close", + "gzipReader.Read", + "headerFieldNameError.Error", + "headerFieldValueError.Error", + "noDialClientConnPool.GetClientConn", + "noDialH2RoundTripper.RoundTrip", + "pipe.Read", + "priorityWriteScheduler.CloseStream", + "priorityWriteScheduler.OpenStream", + "pseudoHeaderError.Error", + "requestBody.Close", + "requestBody.Read", + "responseWriter.Flush", + "responseWriter.FlushError", + "responseWriter.Push", + "responseWriter.SetReadDeadline", + "responseWriter.SetWriteDeadline", + "responseWriter.Write", + "responseWriter.WriteHeader", + "responseWriter.WriteString", + "roundRobinWriteScheduler.OpenStream", + "serverConn.CloseConn", + "serverConn.Flush", + "stickyErrWriter.Write", + "transportResponseBody.Close", + "transportResponseBody.Read", + "writeData.String" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.23.0" + } + ] + }, + { + "id": "GO-2024-3333", + "imports": [ + { + "path": "golang.org/x/net/html", + "symbols": [ + "Parse", + "ParseFragment", + "ParseFragmentWithOptions", + "ParseWithOptions", + "htmlIntegrationPoint", + "inBodyIM", + "inTableIM", + "parseDoctype" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.33.0" + } + ] + }, + { + "id": "GO-2025-3503", + "imports": [ + { + "path": "golang.org/x/net/http/httpproxy", + "symbols": [ + "config.useProxy", + "domainMatch.match" + ] + }, + { + "path": "golang.org/x/net/proxy", + "symbols": [ + "Dial", + "FromEnvironment", + "FromEnvironmentUsing", + "PerHost.AddFromString", + "PerHost.Dial", + "PerHost.DialContext", + "PerHost.dialerForRequest" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.36.0" + } + ] + }, + { + "id": "GO-2025-3595", + "imports": [ + { + "path": "golang.org/x/net/html", + "symbols": [ + "Parse", + "ParseFragment", + "ParseFragmentWithOptions", + "ParseWithOptions", + "Tokenizer.Next", + "Tokenizer.readStartTag" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.38.0" + } + ] + }, + { + "id": "GO-2026-4440", + "imports": [ + { + "path": "golang.org/x/net/html", + "symbols": [ + "Parse", + "ParseFragment", + "ParseFragmentWithOptions", + "ParseWithOptions", + "parser.parse" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.45.0" + } + ] + }, + { + "id": "GO-2026-4441", + "imports": [ + { + "path": "golang.org/x/net/html", + "symbols": [ + "Parse", + "ParseFragment", + "ParseFragmentWithOptions", + "ParseWithOptions", + "inRowIM" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.45.0" + } + ] + }, + { + "id": "GO-2026-4559", + "imports": [ + { + "path": "golang.org/x/net/http2", + "symbols": [ + "ClientConn.Close", + "ClientConn.Ping", + "ClientConn.RoundTrip", + "ClientConn.Shutdown", + "ConfigureServer", + "ConfigureTransport", + "ConfigureTransports", + "ConnectionError.Error", + "ErrCode.String", + "FrameHeader.String", + "FrameType.String", + "FrameWriteRequest.String", + "Framer.ReadFrame", + "Framer.ReadFrameForHeader", + "Framer.ReadFrameHeader", + "Framer.WriteContinuation", + "Framer.WriteData", + "Framer.WriteDataPadded", + "Framer.WriteGoAway", + "Framer.WriteHeaders", + "Framer.WritePing", + "Framer.WritePriority", + "Framer.WritePriorityUpdate", + "Framer.WritePushPromise", + "Framer.WriteRSTStream", + "Framer.WriteRawFrame", + "Framer.WriteSettings", + "Framer.WriteSettingsAck", + "Framer.WriteWindowUpdate", + "GoAwayError.Error", + "ReadFrameHeader", + "Server.ServeConn", + "Setting.String", + "SettingID.String", + "SettingsFrame.ForeachSetting", + "StreamError.Error", + "Transport.CloseIdleConnections", + "Transport.NewClientConn", + "Transport.RoundTrip", + "Transport.RoundTripOpt", + "bufferedWriter.Flush", + "bufferedWriter.Write", + "bufferedWriterTimeoutWriter.Write", + "chunkWriter.Write", + "clientConnPool.GetClientConn", + "connError.Error", + "dataBuffer.Read", + "duplicatePseudoHeaderError.Error", + "gzipReader.Close", + "gzipReader.Read", + "headerFieldNameError.Error", + "headerFieldValueError.Error", + "netHTTPClientConn.Close", + "netHTTPClientConn.RoundTrip", + "noDialClientConnPool.GetClientConn", + "noDialH2RoundTripper.NewClientConn", + "noDialH2RoundTripper.RoundTrip", + "pipe.Read", + "priorityWriteSchedulerRFC7540.CloseStream", + "priorityWriteSchedulerRFC7540.OpenStream", + "priorityWriteSchedulerRFC9218.OpenStream", + "pseudoHeaderError.Error", + "requestBody.Close", + "requestBody.Read", + "responseWriter.Flush", + "responseWriter.FlushError", + "responseWriter.Push", + "responseWriter.SetReadDeadline", + "responseWriter.SetWriteDeadline", + "responseWriter.Write", + "responseWriter.WriteHeader", + "responseWriter.WriteString", + "roundRobinWriteScheduler.OpenStream", + "serverConn.CloseConn", + "serverConn.Flush", + "stickyErrWriter.Write", + "transportResponseBody.Close", + "transportResponseBody.Read", + "typeFrameParser", + "unencryptedTransport.RoundTrip", + "writeData.String" + ] + } + ], + "ranges": [ + { + "introduced": "0.50.0" + }, + { + "fixed": "0.51.0" + } + ] + }, + { + "id": "GO-2026-4918", + "imports": [ + { + "path": "golang.org/x/net/http2", + "symbols": [ + "Transport.NewClientConn", + "Transport.RoundTrip", + "Transport.RoundTripOpt", + "clientConnPool.GetClientConn", + "clientConnReadLoop.processSettingsNoWrite", + "noDialClientConnPool.GetClientConn", + "noDialH2RoundTripper.NewClientConn", + "noDialH2RoundTripper.RoundTrip", + "unencryptedTransport.RoundTrip" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.53.0" + } + ] + }, + { + "id": "GO-2026-5025", + "imports": [ + { + "path": "golang.org/x/net/html", + "symbols": [ + "Parse", + "ParseFragment", + "ParseFragmentWithOptions", + "ParseWithOptions", + "parser.parse" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.55.0" + } + ] + }, + { + "id": "GO-2026-5026", + "imports": [ + { + "path": "golang.org/x/net/idna", + "symbols": [ + "Profile.ToASCII", + "Profile.ToUnicode", + "Profile.process", + "ToASCII", + "ToUnicode" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.55.0" + } + ] + }, + { + "id": "GO-2026-5027", + "imports": [ + { + "path": "golang.org/x/net/html", + "symbols": [ + "Parse", + "ParseFragment", + "ParseFragmentWithOptions", + "ParseWithOptions", + "parser.parse" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.55.0" + } + ] + }, + { + "id": "GO-2026-5028", + "imports": [ + { + "path": "golang.org/x/net/html", + "symbols": [ + "Parse", + "ParseFragment", + "ParseFragmentWithOptions", + "ParseWithOptions", + "parser.parse" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.55.0" + } + ] + }, + { + "id": "GO-2026-5029", + "imports": [ + { + "path": "golang.org/x/net/html", + "symbols": [ + "Parse", + "ParseFragment", + "ParseFragmentWithOptions", + "ParseWithOptions", + "parser.parse" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.55.0" + } + ] + }, + { + "id": "GO-2026-5030", + "imports": [ + { + "path": "golang.org/x/net/html", + "symbols": [ + "Parse", + "ParseFragment", + "ParseFragmentWithOptions", + "ParseWithOptions", + "parser.parse" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.55.0" + } + ] + }, + { + "id": "GO-2026-5942", + "imports": [ + { + "path": "golang.org/x/net/dns/dnsmessage", + "symbols": [ + "Message.Unpack", + "Parser.Additional", + "Parser.AllAdditionals", + "Parser.AllAnswers", + "Parser.AllAuthorities", + "Parser.Answer", + "Parser.Authority", + "Parser.HTTPSResource", + "Parser.SVCBResource", + "unpackSVCBResource" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.56.0" + } + ] + }, + { + "id": "GO-2026-6603", + "imports": [ + { + "path": "golang.org/x/net/http2", + "symbols": [ + "ClientConn.Close", + "ClientConn.Ping", + "ClientConn.RoundTrip", + "ClientConn.Shutdown", + "ConfigureServer", + "ConfigureTransport", + "ConfigureTransports", + "ConnectionError.Error", + "ErrCode.String", + "FrameHeader.String", + "FrameType.String", + "FrameWriteRequest.String", + "Framer.ReadFrame", + "Framer.ReadFrameForHeader", + "Framer.ReadFrameHeader", + "Framer.WriteContinuation", + "Framer.WriteData", + "Framer.WriteDataPadded", + "Framer.WriteGoAway", + "Framer.WriteHeaders", + "Framer.WritePing", + "Framer.WritePriority", + "Framer.WritePriorityUpdate", + "Framer.WritePushPromise", + "Framer.WriteRSTStream", + "Framer.WriteRawFrame", + "Framer.WriteSettings", + "Framer.WriteSettingsAck", + "Framer.WriteWindowUpdate", + "Framer.readMetaFrame", + "GoAwayError.Error", + "ReadFrameHeader", + "Server.ServeConn", + "Setting.String", + "SettingID.String", + "SettingsFrame.ForeachSetting", + "StreamError.Error", + "Transport.CloseIdleConnections", + "Transport.NewClientConn", + "Transport.RoundTrip", + "Transport.RoundTripOpt", + "bufferedWriter.Flush", + "bufferedWriter.Write", + "bufferedWriterTimeoutWriter.Write", + "chunkWriter.Write", + "clientConnPool.GetClientConn", + "connError.Error", + "dataBuffer.Read", + "duplicatePseudoHeaderError.Error", + "gzipReader.Close", + "gzipReader.Read", + "headerFieldNameError.Error", + "headerFieldValueError.Error", + "netHTTPClientConn.Close", + "netHTTPClientConn.RoundTrip", + "noDialClientConnPool.GetClientConn", + "noDialH2RoundTripper.NewClientConn", + "noDialH2RoundTripper.RoundTrip", + "pipe.Read", + "priorityWriteSchedulerRFC7540.CloseStream", + "priorityWriteSchedulerRFC7540.OpenStream", + "priorityWriteSchedulerRFC9218.OpenStream", + "pseudoHeaderError.Error", + "requestBody.Close", + "requestBody.Read", + "responseWriter.Flush", + "responseWriter.FlushError", + "responseWriter.Push", + "responseWriter.SetReadDeadline", + "responseWriter.SetWriteDeadline", + "responseWriter.Write", + "responseWriter.WriteHeader", + "responseWriter.WriteString", + "roundRobinWriteScheduler.OpenStream", + "serverConn.CloseConn", + "serverConn.Flush", + "stickyErrWriter.Write", + "transportResponseBody.Close", + "transportResponseBody.Read", + "unencryptedTransport.RoundTrip", + "writeData.String" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.60.0" + } + ] + }, + { + "id": "GO-2026-6610", + "imports": [ + { + "path": "golang.org/x/net/http2", + "symbols": [ + "Transport.NewClientConn", + "Transport.RoundTrip", + "Transport.RoundTripOpt", + "clientConnPool.GetClientConn", + "clientConnReadLoop.handleResponse", + "noDialClientConnPool.GetClientConn", + "noDialH2RoundTripper.NewClientConn", + "noDialH2RoundTripper.RoundTrip", + "unencryptedTransport.RoundTrip" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.60.0" + } + ] + }, + { + "id": "GO-2026-6611", + "imports": [ + { + "path": "golang.org/x/net/http2", + "symbols": [ + "ClientConn.Close", + "ClientConn.Ping", + "ClientConn.RoundTrip", + "ClientConn.Shutdown", + "ClientConn.addStreamLocked", + "ConfigureServer", + "ConfigureTransport", + "ConfigureTransports", + "ConnectionError.Error", + "ErrCode.String", + "FrameHeader.String", + "FrameType.String", + "FrameWriteRequest.Consume", + "FrameWriteRequest.String", + "Framer.ReadFrame", + "Framer.ReadFrameForHeader", + "Framer.ReadFrameHeader", + "Framer.WriteContinuation", + "Framer.WriteData", + "Framer.WriteDataPadded", + "Framer.WriteGoAway", + "Framer.WriteHeaders", + "Framer.WritePing", + "Framer.WritePriority", + "Framer.WritePriorityUpdate", + "Framer.WritePushPromise", + "Framer.WriteRSTStream", + "Framer.WriteRawFrame", + "Framer.WriteSettings", + "Framer.WriteSettingsAck", + "Framer.WriteWindowUpdate", + "GoAwayError.Error", + "ReadFrameHeader", + "Server.ServeConn", + "Server.serveConn", + "Setting.String", + "SettingID.String", + "SettingsFrame.ForeachSetting", + "StreamError.Error", + "Transport.CloseIdleConnections", + "Transport.NewClientConn", + "Transport.RoundTrip", + "Transport.RoundTripOpt", + "Transport.newClientConn", + "bufferedWriter.Flush", + "bufferedWriter.Write", + "bufferedWriterTimeoutWriter.Write", + "chunkWriter.Write", + "clientConnPool.GetClientConn", + "clientConnReadLoop.processSettingsNoWrite", + "clientConnReadLoop.processWindowUpdate", + "clientConnReadLoop.streamByID", + "clientStream.awaitFlowControl", + "clientStream.cleanupWriteRequest", + "connError.Error", + "dataBuffer.Read", + "duplicatePseudoHeaderError.Error", + "gzipReader.Close", + "gzipReader.Read", + "headerFieldNameError.Error", + "headerFieldValueError.Error", + "netHTTPClientConn.Close", + "netHTTPClientConn.RoundTrip", + "noDialClientConnPool.GetClientConn", + "noDialH2RoundTripper.NewClientConn", + "noDialH2RoundTripper.RoundTrip", + "outflow.add", + "outflow.available", + "outflow.setConnFlow", + "outflow.take", + "pipe.Read", + "priorityWriteSchedulerRFC7540.CloseStream", + "priorityWriteSchedulerRFC7540.OpenStream", + "priorityWriteSchedulerRFC7540.Pop", + "priorityWriteSchedulerRFC9218.OpenStream", + "priorityWriteSchedulerRFC9218.Pop", + "pseudoHeaderError.Error", + "randomWriteScheduler.Pop", + "requestBody.Close", + "requestBody.Read", + "responseWriter.Flush", + "responseWriter.FlushError", + "responseWriter.Push", + "responseWriter.SetReadDeadline", + "responseWriter.SetWriteDeadline", + "responseWriter.Write", + "responseWriter.WriteHeader", + "responseWriter.WriteString", + "roundRobinWriteScheduler.OpenStream", + "roundRobinWriteScheduler.Pop", + "serverConn.CloseConn", + "serverConn.Flush", + "serverConn.newStream", + "serverConn.processSettingInitialWindowSize", + "serverConn.processWindowUpdate", + "serverConn.scheduleFrameWrite", + "stickyErrWriter.Write", + "transportResponseBody.Close", + "transportResponseBody.Read", + "unencryptedTransport.RoundTrip", + "writeData.String" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.60.0" + } + ] + }, + { + "id": "GO-2026-6612", + "imports": [ + { + "path": "golang.org/x/net/http2", + "symbols": [ + "ClientConn.Close", + "ClientConn.Ping", + "ClientConn.RoundTrip", + "ClientConn.Shutdown", + "ConfigureServer", + "ConfigureTransport", + "ConfigureTransports", + "ConnectionError.Error", + "ErrCode.String", + "FrameHeader.String", + "FrameType.String", + "FrameWriteRequest.String", + "Framer.ReadFrame", + "Framer.ReadFrameForHeader", + "Framer.ReadFrameHeader", + "Framer.WriteContinuation", + "Framer.WriteData", + "Framer.WriteDataPadded", + "Framer.WriteGoAway", + "Framer.WriteHeaders", + "Framer.WritePing", + "Framer.WritePriority", + "Framer.WritePriorityUpdate", + "Framer.WritePushPromise", + "Framer.WriteRSTStream", + "Framer.WriteRawFrame", + "Framer.WriteSettings", + "Framer.WriteSettingsAck", + "Framer.WriteWindowUpdate", + "GoAwayError.Error", + "ReadFrameHeader", + "Server.ServeConn", + "Setting.String", + "SettingID.String", + "SettingsFrame.ForeachSetting", + "StreamError.Error", + "Transport.CloseIdleConnections", + "Transport.NewClientConn", + "Transport.RoundTrip", + "Transport.RoundTripOpt", + "bufferedWriter.Flush", + "bufferedWriter.Write", + "bufferedWriterTimeoutWriter.Write", + "chunkWriter.Write", + "clientConnPool.GetClientConn", + "connError.Error", + "dataBuffer.Read", + "duplicatePseudoHeaderError.Error", + "gzipReader.Close", + "gzipReader.Read", + "headerFieldNameError.Error", + "headerFieldValueError.Error", + "netHTTPClientConn.Close", + "netHTTPClientConn.RoundTrip", + "noDialClientConnPool.GetClientConn", + "noDialH2RoundTripper.NewClientConn", + "noDialH2RoundTripper.RoundTrip", + "pipe.Read", + "priorityWriteSchedulerRFC7540.CloseStream", + "priorityWriteSchedulerRFC7540.OpenStream", + "priorityWriteSchedulerRFC9218.OpenStream", + "pseudoHeaderError.Error", + "requestBody.Close", + "requestBody.Read", + "responseWriter.Flush", + "responseWriter.FlushError", + "responseWriter.Push", + "responseWriter.SetReadDeadline", + "responseWriter.SetWriteDeadline", + "responseWriter.Write", + "responseWriter.WriteHeader", + "responseWriter.WriteString", + "roundRobinWriteScheduler.OpenStream", + "serverConn.CloseConn", + "serverConn.Flush", + "serverConn.closeStream", + "serverConn.handlerDone", + "serverConn.newWriterAndRequest", + "serverConn.newWriterAndRequestNoBody", + "serverConn.noteBodyRead", + "serverConn.processHeaders", + "serverConn.runHandler", + "serverConn.scheduleHandler", + "stickyErrWriter.Write", + "transportResponseBody.Close", + "transportResponseBody.Read", + "unencryptedTransport.RoundTrip", + "writeData.String" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.60.0" + } + ] + }, + { + "id": "GO-2026-6617", + "imports": [ + { + "path": "golang.org/x/net/http2", + "symbols": [ + "ClientConn.Close", + "ClientConn.Ping", + "ClientConn.RoundTrip", + "ClientConn.Shutdown", + "ConfigureServer", + "ConfigureTransport", + "ConfigureTransports", + "ConnectionError.Error", + "ErrCode.String", + "FrameHeader.String", + "FrameType.String", + "FrameWriteRequest.String", + "Framer.ReadFrame", + "Framer.ReadFrameForHeader", + "Framer.ReadFrameHeader", + "Framer.WriteContinuation", + "Framer.WriteData", + "Framer.WriteDataPadded", + "Framer.WriteGoAway", + "Framer.WriteHeaders", + "Framer.WritePing", + "Framer.WritePriority", + "Framer.WritePriorityUpdate", + "Framer.WritePushPromise", + "Framer.WriteRSTStream", + "Framer.WriteRawFrame", + "Framer.WriteSettings", + "Framer.WriteSettingsAck", + "Framer.WriteWindowUpdate", + "GoAwayError.Error", + "ReadFrameHeader", + "Server.ServeConn", + "Setting.String", + "SettingID.String", + "SettingsFrame.ForeachSetting", + "StreamError.Error", + "Transport.CloseIdleConnections", + "Transport.NewClientConn", + "Transport.RoundTrip", + "Transport.RoundTripOpt", + "bufferedWriter.Flush", + "bufferedWriter.Write", + "bufferedWriterTimeoutWriter.Write", + "chunkWriter.Write", + "clientConnPool.GetClientConn", + "connError.Error", + "dataBuffer.Read", + "duplicatePseudoHeaderError.Error", + "gzipReader.Close", + "gzipReader.Read", + "headerFieldNameError.Error", + "headerFieldValueError.Error", + "netHTTPClientConn.Close", + "netHTTPClientConn.RoundTrip", + "noDialClientConnPool.GetClientConn", + "noDialH2RoundTripper.NewClientConn", + "noDialH2RoundTripper.RoundTrip", + "pipe.Read", + "priorityWriteSchedulerRFC7540.CloseStream", + "priorityWriteSchedulerRFC7540.OpenStream", + "priorityWriteSchedulerRFC9218.OpenStream", + "pseudoHeaderError.Error", + "requestBody.Close", + "requestBody.Read", + "responseWriter.Flush", + "responseWriter.FlushError", + "responseWriter.Push", + "responseWriter.SetReadDeadline", + "responseWriter.SetWriteDeadline", + "responseWriter.Write", + "responseWriter.WriteHeader", + "responseWriter.WriteString", + "roundRobinWriteScheduler.OpenStream", + "serverConn.CloseConn", + "serverConn.Flush", + "serverConn.processSetting", + "serverConn.startFrameWrite", + "stickyErrWriter.Write", + "transportResponseBody.Close", + "transportResponseBody.Read", + "unencryptedTransport.RoundTrip", + "writeData.String" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.60.0" + } + ] + } + ], + "golang.org/x/oauth2": [ + { + "id": "GO-2025-3488", + "imports": [ + { + "path": "golang.org/x/oauth2/jws", + "symbols": [ + "Verify" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.27.0" + } + ] + } + ], + "golang.org/x/sys": [ + { + "id": "GO-2022-0493", + "imports": [ + { + "path": "golang.org/x/sys/unix", + "symbols": [ + "Faccessat" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.0.0-20220412211240-33da011f77ad" + } + ] + }, + { + "id": "GO-2026-5024", + "imports": [ + { + "path": "golang.org/x/sys/windows", + "symbols": [ + "NewNTUnicodeString" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.44.0" + } + ] + } + ], + "golang.org/x/text": [ + { + "id": "GO-2020-0015", + "imports": [ + { + "path": "golang.org/x/text/encoding/unicode", + "symbols": [ + "bomOverride.Transform", + "utf16Decoder.Transform" + ] + }, + { + "path": "golang.org/x/text/transform", + "symbols": [ + "String" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.3.3" + } + ] + }, + { + "id": "GO-2021-0113", + "imports": [ + { + "path": "golang.org/x/text/language", + "symbols": [ + "MatchStrings", + "MustParse", + "Parse", + "ParseAcceptLanguage" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.3.7" + } + ] + }, + { + "id": "GO-2022-1059", + "imports": [ + { + "path": "golang.org/x/text/language", + "symbols": [ + "MatchStrings", + "ParseAcceptLanguage" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.3.8" + } + ] + }, + { + "id": "GO-2026-5970", + "imports": [ + { + "path": "golang.org/x/text/unicode/norm", + "symbols": [ + "Form.Append", + "Form.AppendString", + "Form.Bytes", + "Form.FirstBoundary", + "Form.FirstBoundaryInString", + "Form.IsNormal", + "Form.IsNormalString", + "Form.LastBoundary", + "Form.NextBoundary", + "Form.NextBoundaryInString", + "Form.Properties", + "Form.PropertiesString", + "Form.QuickSpan", + "Form.QuickSpanString", + "Form.Span", + "Form.SpanString", + "Form.String", + "Form.Transform", + "Iter.Init", + "Iter.InitString", + "Iter.Next", + "Iter.Seek", + "nextComposed", + "normReader.Read", + "normWriter.Write" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.39.0" + } + ] + }, + { + "id": "GO-2026-6629", + "imports": [ + { + "path": "golang.org/x/text/secure/precis", + "symbols": [ + "Profile.Append", + "Profile.AppendCompareKey", + "Profile.Bytes", + "Profile.Compare", + "Profile.CompareKey", + "Profile.NewTransformer", + "Profile.String", + "Transformer.Bytes", + "Transformer.String", + "Transformer.Transform", + "nickAdditionalMapping.Transform" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "0.41.0" + } + ] + } + ], + "google.golang.org/grpc": [ + { + "id": "GO-2023-2153", + "imports": [ + { + "path": "google.golang.org/grpc/internal/transport", + "symbols": [ + "NewServerTransport" + ] + }, + { + "path": "google.golang.org/grpc", + "symbols": [ + "NewServer", + "Server.Serve", + "Server.initServerWorkers" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "1.56.3" + }, + { + "introduced": "1.57.0" + }, + { + "fixed": "1.57.1" + }, + { + "introduced": "1.58.0" + }, + { + "fixed": "1.58.3" + } + ] + }, + { + "id": "GO-2024-2978", + "imports": [ + { + "path": "google.golang.org/grpc/metadata", + "symbols": [ + "MD.String" + ] + } + ], + "ranges": [ + { + "introduced": "1.64.0" + }, + { + "fixed": "1.64.1" + } + ] + }, + { + "id": "GO-2026-4762", + "imports": [ + { + "path": "google.golang.org/grpc", + "symbols": [ + "Server.Serve", + "Server.ServeHTTP", + "Server.handleStream" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "1.79.3" + } + ] + }, + { + "id": "GO-2026-6061", + "imports": [ + { + "path": "google.golang.org/grpc/internal/transport", + "symbols": [ + "ClientStream.Close", + "ClientStream.Header", + "ClientStream.Read", + "ClientStream.RecvCompress", + "ClientStream.TrailersOnly", + "ClientStream.Write", + "NewHTTP2Client", + "NewServerTransport", + "ServerStream.Read", + "ServerStream.SendHeader", + "ServerStream.Write", + "ServerStream.WriteStatus", + "Stream.ReadMessageHeader", + "controlBuffer.executeAndPut", + "controlBuffer.getOnceLocked", + "http2Client.Close", + "http2Client.GracefulClose", + "http2Client.NewStream", + "http2Server.Drain", + "http2Server.HandleStreams", + "recvBufferReader.Read", + "recvBufferReader.ReadMessageHeader", + "transportReader.Read", + "transportReader.ReadMessageHeader" + ] + }, + { + "path": "google.golang.org/grpc/internal/xds/rbac", + "symbols": [ + "ChainEngine.IsAuthorized", + "NewChainEngine", + "matchersFromPermissions", + "matchersFromPrincipals" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "1.82.1" + } + ] + }, + { + "id": "GO-2026-6348", + "imports": [ + { + "path": "google.golang.org/grpc/internal/transport", + "symbols": [ + "ClientStream.Close", + "ClientStream.Header", + "ClientStream.Read", + "ClientStream.RecvCompress", + "ClientStream.TrailersOnly", + "NewHTTP2Client", + "NewServerTransport", + "ServerStream.Read", + "Stream.ReadMessageHeader", + "http2Client.Close", + "http2Client.GracefulClose", + "http2Client.NewStream", + "http2Client.newStream", + "http2Server.Close", + "http2Server.HandleStreams", + "http2Server.operateHeaders", + "recvBuffer.init", + "recvBuffer.load", + "recvBuffer.put", + "recvBufferReader.Read", + "recvBufferReader.ReadMessageHeader", + "serverHandlerTransport.HandleStreams", + "transportReader.Read", + "transportReader.ReadMessageHeader" + ] + }, + { + "path": "google.golang.org/grpc/mem", + "symbols": [ + "BufferSlice.MaterializeToBuffer", + "Copy", + "IsBelowBufferPoolingThreshold", + "NewBuffer", + "ReadAll", + "writer.Write" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "1.83.1" + } + ] + }, + { + "id": "GO-2026-6441", + "imports": [ + { + "path": "google.golang.org/grpc/internal/xds/httpfilter/rbac", + "symbols": [ + "builder.ParseFilterConfig", + "builder.ParseFilterConfigOverride", + "parseConfig" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "1.83.1" + } + ] + }, + { + "id": "GO-2026-6443", + "imports": [ + { + "path": "google.golang.org/grpc/internal/transport", + "symbols": [ + "http2Server.HandleStreams", + "http2Server.operateHeaders" + ] + }, + { + "path": "google.golang.org/grpc/internal/xds/server", + "symbols": [ + "RouteAndProcess" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "1.82.2" + }, + { + "introduced": "1.83.0" + }, + { + "fixed": "1.83.2" + }, + { + "introduced": "1.84.0-dev" + }, + { + "fixed": "1.84.0-dev.0.20260825144003-d5a41119e0e3" + }, + { + "introduced": "1.85.0-dev" + }, + { + "fixed": "1.85.0-dev.0.20260825072537-93e31b48545e" + } + ] + } + ], + "google.golang.org/protobuf": [ + { + "id": "GO-2023-1631", + "imports": [ + { + "path": "google.golang.org/protobuf/encoding/prototext", + "symbols": [ + "Unmarshal", + "UnmarshalOptions.Unmarshal", + "UnmarshalOptions.unmarshal" + ] + }, + { + "path": "google.golang.org/protobuf/internal/encoding/text", + "symbols": [ + "Decoder.Peek", + "Decoder.Read", + "parseNumber" + ] + } + ], + "ranges": [ + { + "introduced": "1.29.0" + }, + { + "fixed": "1.29.1" + } + ] + }, + { + "id": "GO-2024-2611", + "imports": [ + { + "path": "google.golang.org/protobuf/encoding/protojson", + "symbols": [ + "Unmarshal", + "UnmarshalOptions.Unmarshal", + "UnmarshalOptions.unmarshal" + ] + }, + { + "path": "google.golang.org/protobuf/internal/encoding/json", + "symbols": [ + "Decoder.Peek", + "Decoder.Read" + ] + } + ], + "ranges": [ + { + "introduced": "0" + }, + { + "fixed": "1.33.0" + } + ] + } + ] + } +} \ No newline at end of file diff --git a/test/projectgraph/public/methods/corpus.txt b/test/projectgraph/public/methods/corpus.txt new file mode 100644 index 00000000..06069a2e --- /dev/null +++ b/test/projectgraph/public/methods/corpus.txt @@ -0,0 +1,23 @@ +# The projects of the method measure, one per line: the language, a name, the +# project as module@version or crate@version, and each dependency that an +# advisory of advisories.json names at its pinned version. +go bluemonday github.com/microcosm-cc/bluemonday@v1.0.16 golang.org/x/net@v0.0.0-20210614182718-04defd469f4e +go goquery github.com/PuerkitoBio/goquery@v1.8.0 golang.org/x/net@v0.0.0-20210916014120-12bc252f5db8 +go colly github.com/gocolly/colly/v2@v2.1.0 github.com/antchfx/xmlquery@v1.2.4 github.com/antchfx/xpath@v1.1.8 golang.org/x/crypto@v0.0.0-20190605123033-f99c8df09eb5 golang.org/x/net@v0.0.0-20200602114024-627f9648deb9 google.golang.org/protobuf@v1.24.0 +go hey github.com/rakyll/hey@v0.1.4 golang.org/x/net@v0.0.0-20181017193950-04a2e542c03f golang.org/x/text@v0.3.0 +go melody github.com/olahol/melody@v1.1.1 github.com/gorilla/websocket@v1.5.0 +go scany github.com/georgysavva/scany/v2@v2.0.0 github.com/jackc/pgx/v5@v5.0.0 golang.org/x/crypto@v0.0.0-20220829220503-c86fa9a7ed90 golang.org/x/sys@v0.0.0-20210823070655-63515b42dcdf golang.org/x/text@v0.3.7 +go fiber github.com/gofiber/fiber/v2@v2.40.0 github.com/valyala/fasthttp@v1.41.0 golang.org/x/sys@v0.0.0-20220811171246-fbc7d0a398ab +go sftp github.com/pkg/sftp@v1.13.5 golang.org/x/crypto@v0.0.0-20211215153901-e495a2d5b3d3 golang.org/x/sys@v0.0.0-20211216021012-1d35b9e2eb4e +go grpc-middleware github.com/grpc-ecosystem/go-grpc-middleware@v1.3.0 github.com/sirupsen/logrus@v1.4.2 golang.org/x/net@v0.0.0-20201021035429-f5854403a974 golang.org/x/oauth2@v0.0.0-20180821212333-d2e6202438be google.golang.org/grpc@v1.29.1 +go go-rabbitmq github.com/wagslane/go-rabbitmq@v0.12.1 github.com/rabbitmq/amqp091-go@v1.5.0 +rust postgres postgres@0.19.7 tokio@1.13.0 tokio-postgres@0.7.17 +rust parking_lot parking_lot@0.11.0 lock_api@0.4.1 +rust native-tls native-tls@0.2.10 openssl@0.10.71 +rust cookie cookie@0.14.4 time@0.2.22 rand@0.8.5 +rust imageproc imageproc@0.21.0 image@0.23.11 rand@0.7.3 rulinalg@0.4.2 +rust cssparser cssparser@0.27.2 smallvec@1.6.0 +rust selectors selectors@0.22.0 smallvec@1.6.0 +rust tokio-postgres tokio-postgres@0.7.7 postgres-protocol@0.6.11 tokio@1.13.0 +rust lettre_email lettre_email@0.9.4 lettre@0.9.5 time@0.1.45 +rust rusqlite_migration rusqlite_migration@0.5.0 rusqlite@0.22.0 diff --git a/test/projectgraph/receivers_test.go b/test/projectgraph/receivers_test.go new file mode 100644 index 00000000..3b7fc6c9 --- /dev/null +++ b/test/projectgraph/receivers_test.go @@ -0,0 +1,387 @@ +package projectgraph + +import ( + "context" + iofs "io/fs" + "os" + "path/filepath" + "regexp" + "testing" + + "github.com/safedep/code/core" + "github.com/safedep/code/fileindex" + "github.com/safedep/code/fileindex/store/memstore" + codefs "github.com/safedep/code/fs" + "github.com/safedep/code/plugin/indexer" + "github.com/safedep/code/projectgraph" + "github.com/safedep/code/projectgraph/reach" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// receiverRow is one form of the receiver of a method call of a type of a +// dependency. want is the answer for the target, as found or +// uncertain/. A row that the link step does not answer yet names the +// issue that fixes it in gap, and its answer before that fix in today. +type receiverRow struct { + name string + source string + target string + want string + gap string + today string + // noDeps leaves the dependency code out of the graph. + noDeps bool +} + +// receiverUnits are the units of dependency code of the receiver rows, by +// language: a directory under testdata/receivers and its unit. +var receiverUnits = map[core.LanguageCode][2]string{ + core.LanguageCodeGo: {"go/dep", "go:example.com/dep@v1.0.0"}, + core.LanguageCodeRust: {"rust/dep", "cargo:dep@1.0.0"}, +} + +const ( + goReceiverManifest = "module app\n\ngo 1.22\n\nrequire example.com/dep v1.0.0\n" + rustReceiverManifest = "[package]\nname = \"app\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\ndep = \"1.0.0\"\n" +) + +var goReceiverRows = []receiverRow{ + { + name: "declared variable", + source: "func run() {\n\tvar t *dep.Thing = dep.NewThing()\n\tt.Do()\n}", + target: "example.com//dep//Thing//Do", + want: "found", + }, + { + name: "parameter", + source: "func run(t *dep.Thing) {\n\tt.Do()\n}", + target: "example.com//dep//Thing//Do", + want: "found", + }, + { + name: "struct field", + source: "type app struct {\n\tt *dep.Thing\n}\n\nfunc run(a *app) {\n\ta.t.Do()\n}", + target: "example.com//dep//Thing//Do", + want: "found", + }, + { + name: "first-party function return", + source: "func make() *dep.Thing { return nil }\n\nfunc run() {\n\tt := make()\n\tt.Do()\n}", + target: "example.com//dep//Thing//Do", + want: "found", + }, + { + name: "external function return", + source: "func run() {\n\tt := dep.NewThing()\n\tt.Do()\n}", + target: "example.com//dep//Thing//Do", + want: "found", + gap: "FOU-543", + today: "uncertain/unknown-receiver", + }, + { + name: "multi-return", + source: "func run() {\n\tt, err := dep.NewPair()\n\tif err != nil {\n\t\treturn\n\t}\n\tt.Do()\n}", + target: "example.com//dep//Thing//Do", + want: "found", + gap: "FOU-543", + today: "uncertain/unknown-receiver", + }, + { + name: "chained call", + source: "func run() {\n\tdep.NewThing().Do()\n}", + target: "example.com//dep//Thing//Do", + want: "found", + gap: "FOU-546", + today: "no-such-target", + }, + { + name: "chained method call", + source: "func run() {\n\tdep.NewThing().Next().Do()\n}", + target: "example.com//dep//Thing//Do", + want: "found", + gap: "FOU-546", + today: "no-such-target", + }, + { + name: "interface return", + source: "func run() {\n\td := dep.NewDoer()\n\td.Do()\n}", + target: "example.com//dep//Thing//Do", + want: "found", + gap: "FOU-543", + today: "uncertain/unknown-receiver", + }, + { + name: "interface return with no implementation", + source: "func run() {\n\tr := dep.NewRunner()\n\tr.Run()\n}", + target: "example.com//other//Job//Run", + want: "uncertain/interface-receiver", + gap: "FOU-543", + today: "uncertain/unknown-receiver", + }, + { + name: "external function return with no dependency code", + source: "func run() {\n\tt := dep.NewThing()\n\tt.Do()\n}", + target: "example.com//dep//Thing//Do", + want: "uncertain/no-dependency-source", + gap: "FOU-543", + today: "uncertain/unknown-receiver", + noDeps: true, + }, +} + +var rustReceiverRows = []receiverRow{ + { + name: "declared variable", + source: "fn run() {\n let mut t: Thing = Thing::new();\n t.insert(1);\n}", + target: "dep//Thing//insert", + want: "found", + }, + { + name: "parameter", + source: "fn run(mut t: Thing) {\n t.insert(1);\n}", + target: "dep//Thing//insert", + want: "found", + gap: "FOU-544", + today: "uncertain/unknown-receiver", + }, + { + name: "reference and generic parameter", + source: "fn run(t: &mut Thing<[u8; 4]>) {\n t.insert([1; 4]);\n}", + target: "dep//Thing//insert", + want: "found", + gap: "FOU-544", + today: "uncertain/unknown-receiver", + }, + { + name: "struct field", + source: "struct App {\n t: Thing,\n}\n\nfn run(a: &mut App) {\n a.t.insert(1);\n}", + target: "dep//Thing//insert", + want: "found", + gap: "FOU-544", + today: "uncertain/no-definition", + }, + { + name: "first-party function return", + source: "fn make() -> Thing {\n Thing::new()\n}\n\nfn run() {\n let mut t = make();\n t.insert(1);\n}", + target: "dep//Thing//insert", + want: "found", + gap: "FOU-544", + today: "uncertain/unknown-receiver", + }, + { + name: "external function return", + source: "fn run() {\n let mut t = dep::new_thing();\n t.insert(1);\n}", + target: "dep//Thing//insert", + want: "found", + gap: "FOU-544", + today: "uncertain/unknown-receiver", + }, + { + name: "multi-return with ?", + source: "fn run() -> Result<(), String> {\n let mut t = dep::load_thing()?;\n t.insert(1);\n Ok(())\n}", + target: "dep//Thing//insert", + want: "found", + gap: "FOU-544", + today: "uncertain/unknown-receiver", + }, + { + name: "option unwrap", + source: "fn run() {\n let mut t = dep::try_thing().unwrap();\n t.insert(1);\n}", + target: "dep//Thing//insert", + want: "found", + gap: "FOU-544", + today: "uncertain/unknown-receiver", + }, + { + name: "chained call", + source: "fn run() {\n dep::new_thing().insert(1);\n}", + target: "dep//Thing//insert", + want: "found", + gap: "FOU-544", + today: "uncertain/unknown-receiver", + }, + { + name: "associated function", + source: "fn run() {\n let mut t = Thing::from_vec(vec![1u8]);\n t.insert(1);\n}", + target: "dep//Thing//insert", + want: "found", + gap: "FOU-544", + today: "uncertain/unknown-receiver", + }, + { + name: "associated function with type arguments", + source: "fn run() {\n let mut t = Thing::::new();\n t.insert(1);\n}", + target: "dep//Thing//insert", + want: "found", + }, + { + name: "clone of a typed value", + source: "fn run() {\n let a: Thing = Thing::new();\n let mut b = a.clone();\n b.insert(1);\n}", + target: "dep//Thing//insert", + want: "found", + gap: "FOU-544", + today: "uncertain/unknown-receiver", + }, + { + name: "trait object return", + source: "fn run() {\n let d = dep::doer();\n d.run();\n}", + target: "dep//Thing//run", + want: "found", + gap: "FOU-544", + today: "uncertain/unknown-receiver", + }, + { + name: "generic bound", + source: "fn run(d: &T) {\n d.run();\n}", + target: "dep//Thing//run", + want: "found", + gap: "FOU-544", + today: "uncertain/unknown-receiver", + }, + { + name: "trait object with no implementation", + source: "fn run(r: &dyn Runner) {\n r.start();\n}", + target: "dep//Job//start", + want: "uncertain/trait-receiver", + gap: "FOU-544", + today: "uncertain/unknown-receiver", + }, +} + +func TestReceiverForms(t *testing.T) { + languages := caseLanguages(t) + for _, tc := range []struct { + language core.LanguageCode + rows []receiverRow + files func(source string) map[string]string + }{ + {core.LanguageCodeGo, goReceiverRows, goReceiverProject}, + {core.LanguageCodeRust, rustReceiverRows, rustReceiverProject}, + } { + for _, row := range tc.rows { + t.Run(string(tc.language)+"/"+row.name, func(t *testing.T) { + got := receiverAnswer(t, languages, tc.language, tc.files(row.source), row) + if row.gap != "" { + t.Logf("known gap, %s fixes it: the answer is %s, want %s", row.gap, got, row.want) + assert.Equal(t, row.today, got, "the answer before %s changed: update the row", row.gap) + return + } + assert.Equal(t, row.want, got) + }) + } + } +} + +// goReceiverProject is a Go project whose main.go holds the source. +func goReceiverProject(source string) map[string]string { + return map[string]string{ + "go.mod": goReceiverManifest, + "main.go": "package main\n\nimport \"example.com/dep\"\n\n" + source + "\n\nfunc main() {}\n", + } +} + +// rustReceiverProject is a Rust crate whose src/main.rs holds the source +// and the uses of the dependency. +func rustReceiverProject(source string) map[string]string { + return map[string]string{ + "Cargo.toml": rustReceiverManifest, + "src/main.rs": "use dep::{Doer, Runner, Thing};\n\n" + source + "\n\nfn main() {}\n", + } +} + +// receiverAnswer links a project of the files with the dependency code of +// the language and answers the target of the row. +func receiverAnswer(t *testing.T, languages []core.Language, language core.LanguageCode, files map[string]string, row receiverRow) string { + dir := t.TempDir() + for p, text := range files { + require.NoError(t, os.MkdirAll(filepath.Dir(filepath.Join(dir, p)), 0o755)) + require.NoError(t, os.WriteFile(filepath.Join(dir, p), []byte(text), 0o644)) + } + cfg := codefs.LocalFileSystemConfig{AppDirectories: []string{dir}, ImportUnits: map[string]string{}} + units := map[string]iofs.FS{} + if !row.noDeps { + u := receiverUnits[language] + depDir, err := filepath.Abs(filepath.Join("testdata", "receivers", filepath.FromSlash(u[0]))) + require.NoError(t, err) + cfg.ImportDirectories = []string{depDir} + cfg.ImportUnits[depDir] = u[1] + units[u[1]] = os.DirFS(depDir) + } + fsys, err := codefs.NewLocalFileSystem(cfg) + require.NoError(t, err) + indexed := indexProject(t, fsys, indexer.Options{Languages: languages, Store: memstore.New(), Imports: len(units) > 0}) + g := linkProject(t, dir, indexed, projectgraph.Options{Units: units}) + source := receiverSource(t, language) + results, err := reach.Reach(context.Background(), g, reach.Query{Targets: targetsOf(t, row.target), Sources: []reach.Source{source}, SourceMode: reach.SourcesOnly}) + require.NoError(t, err) + return outcome(results[0], receiverReasons(t, g, []string{source.Key()})) +} + +// receiverSource is the run function of the main file of the language, +// which the search starts from. +func receiverSource(t *testing.T, language core.LanguageCode) reach.Source { + p := "main.go" + if language == core.LanguageCodeRust { + p = "src/main.rs" + } + s, err := reach.SourceByKey(fileindex.Join(p, "run")) + require.NoError(t, err) + return s +} + +// outcome is the answer for one target: found, not-found, no-such-target, +// or uncertain with its cause. +func outcome(r reach.Result, methods map[string]string) string { + if r.Status != reach.Uncertain { + return string(r.Status) + } + return "uncertain/" + uncertainCause(r, methods) +} + +var reasonText = regexp.MustCompile(`\(([a-z-]+)\)$`) + +// reasonOf names an uncertainty by its kind, and an unresolved call by its +// reason. +func reasonOf(u reach.Uncertainty) string { + if m := reasonText.FindStringSubmatch(u.Text); u.Kind == reach.UnresolvedCall && m != nil { + return m[1] + } + return string(u.Kind) +} + +// receiverReasons returns the reason of each call site that a search from +// the keys reaches, by the method name of the site. An unknown receiver +// wins over another reason of the name. A result lists at most three +// uncertainties of a kind, so the reasons come from the whole list of an +// exploration. +func receiverReasons(t testing.TB, g *projectgraph.Graph, from []string) map[string]string { + e, err := reach.Explore(context.Background(), g, from, 0) + require.NoError(t, err) + out := map[string]string{} + for _, u := range e.Uncertainties { + if u.Kind != reach.UnresolvedCall || u.Method == "" { + continue + } + if reason := reasonOf(u); out[u.Method] == "" || reason == string(fileindex.UnresolvedUnknownReceiver) { + out[u.Method] = reason + } + } + return out +} + +// uncertainCause names the cause of an uncertain answer. A reached call of +// the method of the target whose receiver the link step does not know +// comes first. Else the cause is the first uncertainty of the result that +// the root fallback does not explain. +func uncertainCause(r reach.Result, methods map[string]string) string { + if reason, ok := methods[fileindex.Last(r.Target.Key())]; ok { + return reason + } + for _, u := range r.Uncertainties { + if u.Kind != reach.RootFallback { + return reasonOf(u) + } + } + return string(reach.RootFallback) +} diff --git a/test/projectgraph/report.md b/test/projectgraph/report.md index a4f2cce0..ebc2e78a 100644 --- a/test/projectgraph/report.md +++ b/test/projectgraph/report.md @@ -854,3 +854,76 @@ cases and 82.2% for Jelly on swarm-js. This report did not run PyCG or Jelly, and its exact-cases metric counts whole cases, so the numbers do not compare one to one. The recall gate of the design needs a run of PyCG and Jelly on the same cases. + +## Method targets + +This section measures how often a target of a vulnerable function or +method of a dependency ends uncertain on real projects. `README.md` says +how to run it. The "before" run is from the commit that adds this +section, on 2026-10-19, on the machine above. + +### The corpus + +`public/methods/corpus.txt` lists 10 Go projects and 10 Rust projects. +Each one depends on a module or a crate with function or method symbols +in an advisory. `public/methods/advisories.json` holds the advisories of +these dependencies: the Go vulndb (`vuln.go.dev`) and RustSec through +OSV, as of 2026-10-19. + +| Language | Projects | Dependencies with an advisory | +| --- | --- | --- | +| Go | bluemonday v1.0.16, goquery v1.8.0, colly v2.1.0, hey v0.1.4, melody v1.1.1, scany v2.0.0, fiber v2.40.0, sftp v1.13.5, go-grpc-middleware v1.3.0, go-rabbitmq v0.12.1 | golang.org/x/net, golang.org/x/crypto, golang.org/x/text, golang.org/x/sys, golang.org/x/oauth2, github.com/antchfx/xpath, github.com/antchfx/xmlquery, google.golang.org/protobuf, google.golang.org/grpc, github.com/gorilla/websocket, github.com/jackc/pgx/v5, github.com/valyala/fasthttp, github.com/sirupsen/logrus, github.com/rabbitmq/amqp091-go | +| Rust | postgres 0.19.7, parking_lot 0.11.0, native-tls 0.2.10, cookie 0.14.4, imageproc 0.21.0, cssparser 0.27.2, selectors 0.22.0, tokio-postgres 0.7.7, lettre_email 0.9.4, rusqlite_migration 0.5.0 | tokio, tokio-postgres, postgres-protocol, lock_api, openssl, time, rand, image, rulinalg, smallvec, lettre, rusqlite | + +A Go dependency has the version that the go.mod of the project names. A +Rust dependency has the newest version that the requirement of the +Cargo.toml admits and that an advisory affects, as a lock file at a +vulnerable version would pin it. The code of each such dependency is a +unit of the link step. Every symbol of an advisory that affects the +version of a dependency is a target. A Go advisory with no symbol list +covers its whole package and gives no target. A Rust name with type +arguments is no key and gives no target. A Rust name whose last segment +starts with an upper case letter is a type, and the table counts it as +`other`. + +The test runs no network call and the corpus is not in the repository, +so a run needs `scripts/fetch_method_corpus.sh` first. The real Go +standard library is not in the corpus, so the measure has no target of +the standard library. + +### What the columns mean + +A target ends as `found`, `not-found`, `no-such-target` or `uncertain`. +An uncertain target has one cause. When the search from the roots +reaches an unresolved call whose method name is the last segment of the +target, the cause is the reason of that call, and `unknown-receiver` +wins over another reason. Else the cause is the first uncertainty of the +result, as `budget`, `ambiguous` or `computed-name`. A library project +has no main, so every exported symbol is a root. + +### Before + +| Language | Kind | Run | Targets | found | not-found | no-such-target | unknown-receiver | no-definition | budget | ambiguous | computed-name | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| Go | function | before | 256 | 13 | 0 | 7 | 3 | 1 | 145 | 53 | 34 | +| Go | method | before | 1298 | 29 | 2 | 15 | 294 | 119 | 576 | 158 | 105 | +| Rust | function | before | 13 | 0 | 0 | 10 | 3 | 0 | 0 | 0 | 0 | +| Rust | method | before | 38 | 0 | 1 | 19 | 13 | 5 | 0 | 0 | 0 | +| Rust | other | before | 5 | 0 | 0 | 5 | 0 | 0 | 0 | 0 | 0 | + +The share of the method targets that end as `unknown-receiver` is 0.227 +(294/1298) for Go and 0.342 (13/38) for Rust. + +- Go: `budget` is the largest cause. The index of each + `idna/tables*.go` of x/net hits a limit, so the file is truncated. Each + `gen.go` of x/net has `//go:build ignore` and `package main`, so the + link step records a truncation for it. A `_test.go` file of x/crypto is + truncated too. The budget of a file of a unit concerns every target + once the search enters the unit. + hey has 47 truncations and scany 55. +- Go: an `ambiguous` or a `computed-name` call concerns every target. + These calls come from the code of a dependency unit, as pgx and + amqp091-go, and from the project, as fiber and go-grpc-middleware. +- Rust: the link step does not link the code of a `cargo:` unit, so the + graph holds no edge into the crate. A target that no project file names + is `no-such-target`. diff --git a/test/projectgraph/testdata/receivers/go/dep/dep.go b/test/projectgraph/testdata/receivers/go/dep/dep.go new file mode 100644 index 00000000..599f9c1c --- /dev/null +++ b/test/projectgraph/testdata/receivers/go/dep/dep.go @@ -0,0 +1,34 @@ +package dep + +// Thing is a type of the dependency with methods. +type Thing struct { + Child *Thing +} + +// NewThing returns a Thing. +func NewThing() *Thing { return &Thing{} } + +// NewPair returns a Thing and an error. +func NewPair() (*Thing, error) { return &Thing{}, nil } + +// Do is the method that the receiver forms call. +func (t *Thing) Do() {} + +// Next returns the child of the Thing. +func (t *Thing) Next() *Thing { return t.Child } + +// Doer is an interface that Thing implements. +type Doer interface { + Do() +} + +// NewDoer returns a Doer that holds a Thing. +func NewDoer() Doer { return &Thing{} } + +// Runner is an interface that no type of the graph implements. +type Runner interface { + Run() +} + +// NewRunner returns a Runner of another module. +func NewRunner() Runner { return nil } diff --git a/test/projectgraph/testdata/receivers/go/dep/go.mod b/test/projectgraph/testdata/receivers/go/dep/go.mod new file mode 100644 index 00000000..59944c3b --- /dev/null +++ b/test/projectgraph/testdata/receivers/go/dep/go.mod @@ -0,0 +1,3 @@ +module example.com/dep + +go 1.22 diff --git a/test/projectgraph/testdata/receivers/rust/dep/Cargo.toml b/test/projectgraph/testdata/receivers/rust/dep/Cargo.toml new file mode 100644 index 00000000..537b4e47 --- /dev/null +++ b/test/projectgraph/testdata/receivers/rust/dep/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "dep" +version = "1.0.0" +edition = "2021" diff --git a/test/projectgraph/testdata/receivers/rust/dep/src/lib.rs b/test/projectgraph/testdata/receivers/rust/dep/src/lib.rs new file mode 100644 index 00000000..33158560 --- /dev/null +++ b/test/projectgraph/testdata/receivers/rust/dep/src/lib.rs @@ -0,0 +1,55 @@ +pub struct Thing { + items: Vec, +} + +impl Thing { + pub fn new() -> Self { + Thing { items: Vec::new() } + } + + pub fn from_vec(items: Vec) -> Self { + Thing { items } + } + + pub fn insert(&mut self, item: A) { + self.items.push(item); + } + + pub fn child(&self) -> &Thing { + self + } +} + +impl Clone for Thing { + fn clone(&self) -> Self { + Thing { items: self.items.clone() } + } +} + +pub fn new_thing() -> Thing { + Thing::new() +} + +pub fn try_thing() -> Option> { + Some(Thing::new()) +} + +pub fn load_thing() -> Result, String> { + Ok(Thing::new()) +} + +pub trait Doer { + fn run(&self); +} + +impl Doer for Thing { + fn run(&self) {} +} + +pub fn doer() -> Box { + Box::new(Thing::::new()) +} + +pub trait Runner { + fn start(&self); +} From ed2291eb097cd9cc33cb68c7362e66a6ed8d7448 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 19 Oct 2026 07:39:34 +0000 Subject: [PATCH 3/9] fix(projectgraph): record a call on an external call result as unknown-receiver (FOU-546) html.NewTokenizer(r).Next() with no dependency code gave no-such-target for golang.org//x//net//html//Tokenizer//Next and left no unresolved record. The walk took Next as a member of the external key of the called name, so the call made an edge to html//NewTokenizer//Next, a key that names no symbol. With no record, a later rule that reads no-such-target as not called for an imported package would hide a real call. The link step now adds an unknown-receiver record with the method name when it takes the method of a call on a call result as a member of the key of the called name. The edge stays, because a Go call of a type name converts a value and the method of the type then runs. A construction gives an instance of the called class, so new Yaml().load() and Yaml().load() of Python or Kotlin get no record. The reach query then answers uncertain at the site of the call through the method scope of the record. Python, JavaScript and Rust chains get the same record. reach answers no-such-target as before when no uncertainty concerns the target. The graph keeps no record of the imports of an external package, so the query cannot tell an imported package from another one, and this change does not turn no-such-target into not-found. The case go/chained-external-call checks the record, the kept edge and the uncertain answer. The cases go/interface-method-set, javascript/commander-action and javascript/references-argument gain the record of a call on a call result. The Go chained rows of the receiver table move to unknown-receiver, and FOU-543 now fixes them. projectgraph.Version is 12. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01PFQbqPqd9xn1fT6hBsyoUm Signed-off-by: Claude --- docs/projectgraph.md | 5 ++ projectgraph/graph.go | 2 +- projectgraph/resolve.go | 73 +++++++++++++++++++ projectgraph/resolve_test.go | 36 +++++++++ .../javascript/barrel-chain/graph.golden.json | 2 +- .../commonjs-require/graph.golden.json | 2 +- .../default-import/graph.golden.json | 2 +- .../esm-named-import/graph.golden.json | 2 +- .../javascript/export-star/graph.golden.json | 2 +- .../exports-conditions/graph.golden.json | 2 +- .../unknown-receiver/graph.golden.json | 2 +- .../javascript/workspaces/graph.golden.json | 2 +- .../python/all-hides/graph.golden.json | 2 +- .../python/assignment/graph.golden.json | 2 +- .../python/class-body/graph.golden.json | 2 +- .../python/decorators/graph.golden.json | 2 +- .../python/external/graph.golden.json | 2 +- .../python/local-receiver/graph.golden.json | 2 +- .../python/member-call/graph.golden.json | 2 +- .../python/missing-file/graph.golden.json | 2 +- .../python/package-init/graph.golden.json | 2 +- .../python/reexport-cycle/graph.golden.json | 2 +- .../python/relative-imports/graph.golden.json | 2 +- .../python/sibling-import/graph.golden.json | 2 +- .../python/src-layout/graph.golden.json | 2 +- .../python/sys-path/graph.golden.json | 2 +- .../python/wildcard/graph.golden.json | 2 +- .../roots/extra-rules/graph.golden.json | 2 +- .../roots/library-surface/graph.golden.json | 2 +- .../roots/main-guard/graph.golden.json | 2 +- .../roots/package-json-bin/graph.golden.json | 2 +- .../roots/package-json-main/graph.golden.json | 2 +- .../procfile-dockerfile/graph.golden.json | 2 +- .../roots/pyproject-scripts/graph.golden.json | 2 +- .../testdata/roots/setup-py/graph.golden.json | 2 +- .../roots/test-file/graph.golden.json | 2 +- .../typescript/js-specifier/graph.golden.json | 2 +- .../tsconfig-paths/graph.golden.json | 2 +- .../typescript/type-only/graph.golden.json | 2 +- .../go/chained-external-call/expect.yaml | 68 +++++++++++++++++ .../go/chained-external-call/project/go.mod | 5 ++ .../go/chained-external-call/project/main.go | 7 ++ .../chained-external-call/project/tokens.go | 11 +++ .../go_github.com_acme_dep_v1.0.0.json | 2 +- .../go_github.com_acme_yamlish_v1.2.0.json | 2 +- .../cases/go/interface-method-set/expect.yaml | 6 +- .../javascript/commander-action/expect.yaml | 8 +- .../summaries/npm_store-kit_1.0.0.json | 2 +- .../summaries/npm_kit_1.0.0.json | 2 +- .../summaries/npm_kit_1.0.0.json | 2 +- .../summaries/npm_plug_1.0.0.json | 2 +- .../summaries/npm_util-kit_1.0.0.json | 2 +- .../npm_lodash._reinterpolate_3.0.0.json | 2 +- .../summaries/npm_lodash_4.17.21.json | 2 +- .../references-argument/expect.yaml | 8 +- .../summaries/pypi_PyYAML_6.0.1.json | 2 +- .../summaries/pypi_badpkg_1.0.0.json | 2 +- .../summaries/pypi_okpkg_1.0.0.json | 2 +- .../summaries/pypi_badpkg_1.0.0.json | 2 +- .../summaries/pypi_okpkg_1.0.0.json | 2 +- .../summaries/pypi_outer_1.0.0.json | 2 +- .../summaries/pypi_requests_2.31.0.json | 2 +- .../summaries/pypi_urllib3_2.0.7.json | 2 +- test/projectgraph/receivers_test.go | 8 +- test/projectgraph/version_test.go | 3 + 65 files changed, 282 insertions(+), 62 deletions(-) create mode 100644 projectgraph/resolve_test.go create mode 100644 test/projectgraph/cases/go/chained-external-call/expect.yaml create mode 100644 test/projectgraph/cases/go/chained-external-call/project/go.mod create mode 100644 test/projectgraph/cases/go/chained-external-call/project/main.go create mode 100644 test/projectgraph/cases/go/chained-external-call/project/tokens.go diff --git a/docs/projectgraph.md b/docs/projectgraph.md index 6cb9852e..ecdd431b 100644 --- a/docs/projectgraph.md +++ b/docs/projectgraph.md @@ -150,6 +150,11 @@ reason: `computed-name`, `dynamic-import`, `unknown-receiver`, `no-definition`, `ambiguous` or `limit`. The site keeps the method name of the call, so the reach query can tell which targets it concerns. +A call on the result of another call, as `html.NewTokenizer(r).Next()`, +needs the type of that result. When the link step does not know it, the +site is an `unknown-receiver` record with the method name, so a target of +that method is `uncertain` and not `no-such-target`. + ## Overlay An overlay is a file of `llm` edges that resolve unresolved sites of a diff --git a/projectgraph/graph.go b/projectgraph/graph.go index 00c72caf..6de75899 100644 --- a/projectgraph/graph.go +++ b/projectgraph/graph.go @@ -17,7 +17,7 @@ import ( // Version is the version of the link rules. Bump it when a rule that makes a // node, an edge, a root or an unresolved site changes, so a diff of two // reports shows the change. -const Version = "11" +const Version = "12" type NodeKind string diff --git a/projectgraph/resolve.go b/projectgraph/resolve.go index 89ab2862..b71015d5 100644 --- a/projectgraph/resolve.go +++ b/projectgraph/resolve.go @@ -114,6 +114,9 @@ func (b *builder) linkCalls(ix *fileindex.Index) { if r.unbound && !b.unboundEdge(ix, call, from, site, r, unknown[[2]int{call.Line, call.Column}]) { continue } + if b.onCallResult(sc.profile, call, r) { + b.addUnresolved(UnresolvedSite{Caller: from, Site: site, Text: call.Callee, Receiver: call.Receiver, Method: call.Method, Reason: fileindex.UnresolvedUnknownReceiver}) + } switch { case r.kind == EdgeDirect && isAssignment(sc, call, r.target): r.kind, r.rule = EdgeAssignment, ruleAssignment @@ -128,6 +131,76 @@ func (b *builder) linkCalls(ix *fileindex.Index) { } } +// onCallResult reports whether a resolution took the method of a call on +// the result of another call as a member of the external key of the called +// name, as html//NewTokenizer//Next for html.NewTokenizer(r).Next(). The +// link step does not know the type of that result, so the key can name no +// symbol, and a target of the method of the real type would read as +// no-such-target. The edge stays, because the called name can be a type, +// as in a Go conversion http.HandlerFunc(f).ServeHTTP(w, r). +// +// A construction gives an instance of the called class, so the key is +// right: new Yaml().load() in Java, or Yaml().load() in Python or Kotlin, +// whose classes start with an upper case letter. A Go name starts with an +// upper case letter when the package exports it, so it tells nothing. +func (b *builder) onCallResult(p *profile, call fileindex.Call, r resolution) bool { + called, ok := calledName(call.Receiver) + if !ok || r.fail != nil || call.Method == "" || b.kindOf(r.key) != NodeExternal { + return false + } + if strings.HasPrefix(call.Receiver, "new ") || !p.typeConversions && startsUpper(called) { + return false + } + return fileindex.Last(r.key) == call.Method && fileindex.Last(fileindex.Parent(r.key)) == called +} + +// calledName returns the name of the call that ends a receiver as written, +// as NewTokenizer for html.NewTokenizer(r) and new for Vec::::new(). +func calledName(receiver string) (string, bool) { + head, ok := strings.CutSuffix(receiver, ")") + if !ok { + return "", false + } + for depth := 1; depth > 0; { + i := strings.LastIndexAny(head, "()") + if i < 0 { + return "", false + } + if head[i] == ')' { + depth++ + } else { + depth-- + } + head = head[:i] + } + head = strings.TrimSuffix(withoutTypeArguments(head), "::") + name := lastWritten(head) + return name, isIdentifier(name) +} + +// withoutTypeArguments removes the type arguments that end a callee as +// written, as [int] of New[int] in Go or of new:: in Rust. +func withoutTypeArguments(callee string) string { + closer := map[byte]byte{']': '[', '>': '<'} + if callee == "" || closer[callee[len(callee)-1]] == 0 { + return callee + } + end, open := callee[len(callee)-1], closer[callee[len(callee)-1]] + depth := 0 + for i := len(callee) - 1; i >= 0; i-- { + switch callee[i] { + case end: + depth++ + case open: + depth-- + if depth == 0 { + return callee[:i] + } + } + } + return callee +} + // thisCall resolves a call of a name that nothing binds in a language where // such a call runs on this, as a Java method that a base of a dependency // or an override in a subclass gives. A builtin function, as println of diff --git a/projectgraph/resolve_test.go b/projectgraph/resolve_test.go new file mode 100644 index 00000000..aa54ba28 --- /dev/null +++ b/projectgraph/resolve_test.go @@ -0,0 +1,36 @@ +package projectgraph + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestCalledName(t *testing.T) { + tests := []struct { + receiver string + name string + ok bool + }{ + {"html.NewTokenizer(r)", "NewTokenizer", true}, + {"html.NewTokenizer(io.MultiReader(a, b))", "NewTokenizer", true}, + {"dep.NewThing().Next()", "Next", true}, + {"pkg.New[int](x)", "New", true}, + {"Vec::::new()", "new", true}, + {"smallvec::new_thing()", "new_thing", true}, + {"yaml.load(f)", "load", true}, + {"z", "", false}, + {"a.b", "", false}, + {"(a)", "", false}, + {"f(x))", "", false}, + } + for _, tt := range tests { + t.Run(tt.receiver, func(t *testing.T) { + name, ok := calledName(tt.receiver) + assert.Equal(t, tt.ok, ok) + if tt.ok { + assert.Equal(t, tt.name, name) + } + }) + } +} diff --git a/projectgraph/testdata/javascript/barrel-chain/graph.golden.json b/projectgraph/testdata/javascript/barrel-chain/graph.golden.json index 63337403..85867615 100644 --- a/projectgraph/testdata/javascript/barrel-chain/graph.golden.json +++ b/projectgraph/testdata/javascript/barrel-chain/graph.golden.json @@ -174,7 +174,7 @@ "src/main.js": "indexed" }, "stats": { - "version": "11", + "version": "12", "files": 4, "nodes": 7, "edges": 6, diff --git a/projectgraph/testdata/javascript/commonjs-require/graph.golden.json b/projectgraph/testdata/javascript/commonjs-require/graph.golden.json index 9bcce014..c6fd6b39 100644 --- a/projectgraph/testdata/javascript/commonjs-require/graph.golden.json +++ b/projectgraph/testdata/javascript/commonjs-require/graph.golden.json @@ -138,7 +138,7 @@ "main.js": "indexed" }, "stats": { - "version": "11", + "version": "12", "files": 3, "nodes": 5, "edges": 4, diff --git a/projectgraph/testdata/javascript/default-import/graph.golden.json b/projectgraph/testdata/javascript/default-import/graph.golden.json index 3ff37a2c..1ff1e467 100644 --- a/projectgraph/testdata/javascript/default-import/graph.golden.json +++ b/projectgraph/testdata/javascript/default-import/graph.golden.json @@ -138,7 +138,7 @@ "make.js": "indexed" }, "stats": { - "version": "11", + "version": "12", "files": 3, "nodes": 5, "edges": 4, diff --git a/projectgraph/testdata/javascript/esm-named-import/graph.golden.json b/projectgraph/testdata/javascript/esm-named-import/graph.golden.json index 3fdccbca..db42551c 100644 --- a/projectgraph/testdata/javascript/esm-named-import/graph.golden.json +++ b/projectgraph/testdata/javascript/esm-named-import/graph.golden.json @@ -166,7 +166,7 @@ "src/util.js": "indexed" }, "stats": { - "version": "11", + "version": "12", "files": 3, "nodes": 6, "edges": 5, diff --git a/projectgraph/testdata/javascript/export-star/graph.golden.json b/projectgraph/testdata/javascript/export-star/graph.golden.json index 02e838f8..dd771d0f 100644 --- a/projectgraph/testdata/javascript/export-star/graph.golden.json +++ b/projectgraph/testdata/javascript/export-star/graph.golden.json @@ -276,7 +276,7 @@ "main.js": "indexed" }, "stats": { - "version": "11", + "version": "12", "files": 5, "nodes": 11, "edges": 10, diff --git a/projectgraph/testdata/javascript/exports-conditions/graph.golden.json b/projectgraph/testdata/javascript/exports-conditions/graph.golden.json index 6c487039..aabf0476 100644 --- a/projectgraph/testdata/javascript/exports-conditions/graph.golden.json +++ b/projectgraph/testdata/javascript/exports-conditions/graph.golden.json @@ -144,7 +144,7 @@ "lib/src/feature/y.js": "indexed" }, "stats": { - "version": "11", + "version": "12", "files": 4, "nodes": 7, "edges": 4, diff --git a/projectgraph/testdata/javascript/unknown-receiver/graph.golden.json b/projectgraph/testdata/javascript/unknown-receiver/graph.golden.json index fc701bf9..bef80997 100644 --- a/projectgraph/testdata/javascript/unknown-receiver/graph.golden.json +++ b/projectgraph/testdata/javascript/unknown-receiver/graph.golden.json @@ -124,7 +124,7 @@ "main.js": "indexed" }, "stats": { - "version": "11", + "version": "12", "files": 1, "nodes": 5, "edges": 3, diff --git a/projectgraph/testdata/javascript/workspaces/graph.golden.json b/projectgraph/testdata/javascript/workspaces/graph.golden.json index 93924508..2ba1864e 100644 --- a/projectgraph/testdata/javascript/workspaces/graph.golden.json +++ b/projectgraph/testdata/javascript/workspaces/graph.golden.json @@ -127,7 +127,7 @@ "packages/core/util.js": "indexed" }, "stats": { - "version": "11", + "version": "12", "files": 3, "nodes": 6, "edges": 5, diff --git a/projectgraph/testdata/python/all-hides/graph.golden.json b/projectgraph/testdata/python/all-hides/graph.golden.json index 307446f7..189e173f 100644 --- a/projectgraph/testdata/python/all-hides/graph.golden.json +++ b/projectgraph/testdata/python/all-hides/graph.golden.json @@ -174,7 +174,7 @@ "pkg/a.py": "indexed" }, "stats": { - "version": "11", + "version": "12", "files": 3, "nodes": 7, "edges": 6, diff --git a/projectgraph/testdata/python/assignment/graph.golden.json b/projectgraph/testdata/python/assignment/graph.golden.json index cca8c3f1..5112173b 100644 --- a/projectgraph/testdata/python/assignment/graph.golden.json +++ b/projectgraph/testdata/python/assignment/graph.golden.json @@ -70,7 +70,7 @@ "main.py": "indexed" }, "stats": { - "version": "11", + "version": "12", "files": 1, "nodes": 3, "edges": 2, diff --git a/projectgraph/testdata/python/class-body/graph.golden.json b/projectgraph/testdata/python/class-body/graph.golden.json index 7befc242..abe0026d 100644 --- a/projectgraph/testdata/python/class-body/graph.golden.json +++ b/projectgraph/testdata/python/class-body/graph.golden.json @@ -224,7 +224,7 @@ "config.py": "indexed" }, "stats": { - "version": "11", + "version": "12", "files": 1, "nodes": 12, "edges": 7, diff --git a/projectgraph/testdata/python/decorators/graph.golden.json b/projectgraph/testdata/python/decorators/graph.golden.json index 6265dbc9..760f11a4 100644 --- a/projectgraph/testdata/python/decorators/graph.golden.json +++ b/projectgraph/testdata/python/decorators/graph.golden.json @@ -194,7 +194,7 @@ "app.py": "indexed" }, "stats": { - "version": "11", + "version": "12", "files": 1, "nodes": 9, "edges": 5, diff --git a/projectgraph/testdata/python/external/graph.golden.json b/projectgraph/testdata/python/external/graph.golden.json index dec84c25..52362e5d 100644 --- a/projectgraph/testdata/python/external/graph.golden.json +++ b/projectgraph/testdata/python/external/graph.golden.json @@ -108,7 +108,7 @@ "main.py": "indexed" }, "stats": { - "version": "11", + "version": "12", "files": 1, "nodes": 5, "edges": 3, diff --git a/projectgraph/testdata/python/local-receiver/graph.golden.json b/projectgraph/testdata/python/local-receiver/graph.golden.json index b7380485..7d03ed8f 100644 --- a/projectgraph/testdata/python/local-receiver/graph.golden.json +++ b/projectgraph/testdata/python/local-receiver/graph.golden.json @@ -145,7 +145,7 @@ "main.py": "indexed" }, "stats": { - "version": "11", + "version": "12", "files": 1, "nodes": 5, "edges": 3, diff --git a/projectgraph/testdata/python/member-call/graph.golden.json b/projectgraph/testdata/python/member-call/graph.golden.json index 268a8edf..f39823b0 100644 --- a/projectgraph/testdata/python/member-call/graph.golden.json +++ b/projectgraph/testdata/python/member-call/graph.golden.json @@ -258,7 +258,7 @@ "parser.py": "indexed" }, "stats": { - "version": "11", + "version": "12", "files": 3, "nodes": 9, "edges": 10, diff --git a/projectgraph/testdata/python/missing-file/graph.golden.json b/projectgraph/testdata/python/missing-file/graph.golden.json index 82613f9a..68d7771e 100644 --- a/projectgraph/testdata/python/missing-file/graph.golden.json +++ b/projectgraph/testdata/python/missing-file/graph.golden.json @@ -55,7 +55,7 @@ "main.py": "cached" }, "stats": { - "version": "11", + "version": "12", "files": 2, "nodes": 2, "edges": 2, diff --git a/projectgraph/testdata/python/package-init/graph.golden.json b/projectgraph/testdata/python/package-init/graph.golden.json index f0351b4b..073ac72e 100644 --- a/projectgraph/testdata/python/package-init/graph.golden.json +++ b/projectgraph/testdata/python/package-init/graph.golden.json @@ -144,7 +144,7 @@ "pkg/loader.py": "indexed" }, "stats": { - "version": "11", + "version": "12", "files": 3, "nodes": 6, "edges": 5, diff --git a/projectgraph/testdata/python/reexport-cycle/graph.golden.json b/projectgraph/testdata/python/reexport-cycle/graph.golden.json index da43f978..1ee0ecb6 100644 --- a/projectgraph/testdata/python/reexport-cycle/graph.golden.json +++ b/projectgraph/testdata/python/reexport-cycle/graph.golden.json @@ -105,7 +105,7 @@ "main.py": "indexed" }, "stats": { - "version": "11", + "version": "12", "files": 3, "nodes": 3, "edges": 3, diff --git a/projectgraph/testdata/python/relative-imports/graph.golden.json b/projectgraph/testdata/python/relative-imports/graph.golden.json index 0802264e..93e6bfe3 100644 --- a/projectgraph/testdata/python/relative-imports/graph.golden.json +++ b/projectgraph/testdata/python/relative-imports/graph.golden.json @@ -154,7 +154,7 @@ "pkg/sub/b.py": "indexed" }, "stats": { - "version": "11", + "version": "12", "files": 3, "nodes": 6, "edges": 4, diff --git a/projectgraph/testdata/python/sibling-import/graph.golden.json b/projectgraph/testdata/python/sibling-import/graph.golden.json index 3d1a15fa..4dd25819 100644 --- a/projectgraph/testdata/python/sibling-import/graph.golden.json +++ b/projectgraph/testdata/python/sibling-import/graph.golden.json @@ -98,7 +98,7 @@ "main.py": "indexed" }, "stats": { - "version": "11", + "version": "12", "files": 2, "nodes": 4, "edges": 3, diff --git a/projectgraph/testdata/python/src-layout/graph.golden.json b/projectgraph/testdata/python/src-layout/graph.golden.json index 140f4fa8..d402f333 100644 --- a/projectgraph/testdata/python/src-layout/graph.golden.json +++ b/projectgraph/testdata/python/src-layout/graph.golden.json @@ -99,7 +99,7 @@ "services/api/src/api/core.py": "indexed" }, "stats": { - "version": "11", + "version": "12", "files": 3, "nodes": 4, "edges": 2, diff --git a/projectgraph/testdata/python/sys-path/graph.golden.json b/projectgraph/testdata/python/sys-path/graph.golden.json index 6c7b8e99..4e5b44bd 100644 --- a/projectgraph/testdata/python/sys-path/graph.golden.json +++ b/projectgraph/testdata/python/sys-path/graph.golden.json @@ -82,7 +82,7 @@ "lib/shared.py": "indexed" }, "stats": { - "version": "11", + "version": "12", "files": 2, "nodes": 3, "edges": 2, diff --git a/projectgraph/testdata/python/wildcard/graph.golden.json b/projectgraph/testdata/python/wildcard/graph.golden.json index 5336f3ba..0ed0e59b 100644 --- a/projectgraph/testdata/python/wildcard/graph.golden.json +++ b/projectgraph/testdata/python/wildcard/graph.golden.json @@ -112,7 +112,7 @@ "util.py": "indexed" }, "stats": { - "version": "11", + "version": "12", "files": 2, "nodes": 4, "edges": 3, diff --git a/projectgraph/testdata/roots/extra-rules/graph.golden.json b/projectgraph/testdata/roots/extra-rules/graph.golden.json index 18a50927..8b0e401c 100644 --- a/projectgraph/testdata/roots/extra-rules/graph.golden.json +++ b/projectgraph/testdata/roots/extra-rules/graph.golden.json @@ -105,7 +105,7 @@ "views.py": "indexed" }, "stats": { - "version": "11", + "version": "12", "files": 3, "nodes": 7, "edges": 0, diff --git a/projectgraph/testdata/roots/library-surface/graph.golden.json b/projectgraph/testdata/roots/library-surface/graph.golden.json index 6da1147a..6376440b 100644 --- a/projectgraph/testdata/roots/library-surface/graph.golden.json +++ b/projectgraph/testdata/roots/library-surface/graph.golden.json @@ -82,7 +82,7 @@ "lib/test_api.py": "indexed" }, "stats": { - "version": "11", + "version": "12", "files": 2, "nodes": 5, "edges": 0, diff --git a/projectgraph/testdata/roots/main-guard/graph.golden.json b/projectgraph/testdata/roots/main-guard/graph.golden.json index 5d43b03c..f4216cab 100644 --- a/projectgraph/testdata/roots/main-guard/graph.golden.json +++ b/projectgraph/testdata/roots/main-guard/graph.golden.json @@ -60,7 +60,7 @@ "lib.py": "indexed" }, "stats": { - "version": "11", + "version": "12", "files": 2, "nodes": 4, "edges": 1, diff --git a/projectgraph/testdata/roots/package-json-bin/graph.golden.json b/projectgraph/testdata/roots/package-json-bin/graph.golden.json index 27bec44c..d3333993 100644 --- a/projectgraph/testdata/roots/package-json-bin/graph.golden.json +++ b/projectgraph/testdata/roots/package-json-bin/graph.golden.json @@ -64,7 +64,7 @@ "lib/run.js": "indexed" }, "stats": { - "version": "11", + "version": "12", "files": 2, "nodes": 3, "edges": 2, diff --git a/projectgraph/testdata/roots/package-json-main/graph.golden.json b/projectgraph/testdata/roots/package-json-main/graph.golden.json index 530a1404..dee2626d 100644 --- a/projectgraph/testdata/roots/package-json-main/graph.golden.json +++ b/projectgraph/testdata/roots/package-json-main/graph.golden.json @@ -154,7 +154,7 @@ "parser.js": "indexed" }, "stats": { - "version": "11", + "version": "12", "files": 4, "nodes": 9, "edges": 3, diff --git a/projectgraph/testdata/roots/procfile-dockerfile/graph.golden.json b/projectgraph/testdata/roots/procfile-dockerfile/graph.golden.json index 94fa85fc..ca146394 100644 --- a/projectgraph/testdata/roots/procfile-dockerfile/graph.golden.json +++ b/projectgraph/testdata/roots/procfile-dockerfile/graph.golden.json @@ -108,7 +108,7 @@ "worker.py": "indexed" }, "stats": { - "version": "11", + "version": "12", "files": 6, "nodes": 7, "edges": 0, diff --git a/projectgraph/testdata/roots/pyproject-scripts/graph.golden.json b/projectgraph/testdata/roots/pyproject-scripts/graph.golden.json index 32ca0b67..b52db490 100644 --- a/projectgraph/testdata/roots/pyproject-scripts/graph.golden.json +++ b/projectgraph/testdata/roots/pyproject-scripts/graph.golden.json @@ -146,7 +146,7 @@ "src/app/tool.py": "indexed" }, "stats": { - "version": "11", + "version": "12", "files": 5, "nodes": 10, "edges": 0, diff --git a/projectgraph/testdata/roots/setup-py/graph.golden.json b/projectgraph/testdata/roots/setup-py/graph.golden.json index 823d259a..6b438460 100644 --- a/projectgraph/testdata/roots/setup-py/graph.golden.json +++ b/projectgraph/testdata/roots/setup-py/graph.golden.json @@ -74,7 +74,7 @@ "tool/main.py": "indexed" }, "stats": { - "version": "11", + "version": "12", "files": 2, "nodes": 4, "edges": 0, diff --git a/projectgraph/testdata/roots/test-file/graph.golden.json b/projectgraph/testdata/roots/test-file/graph.golden.json index 99a21c73..3b77119d 100644 --- a/projectgraph/testdata/roots/test-file/graph.golden.json +++ b/projectgraph/testdata/roots/test-file/graph.golden.json @@ -68,7 +68,7 @@ "tests/test_app.py": "indexed" }, "stats": { - "version": "11", + "version": "12", "files": 1, "nodes": 4, "edges": 0, diff --git a/projectgraph/testdata/typescript/js-specifier/graph.golden.json b/projectgraph/testdata/typescript/js-specifier/graph.golden.json index c8062ccd..d6289607 100644 --- a/projectgraph/testdata/typescript/js-specifier/graph.golden.json +++ b/projectgraph/testdata/typescript/js-specifier/graph.golden.json @@ -82,7 +82,7 @@ "src/util.ts": "indexed" }, "stats": { - "version": "11", + "version": "12", "files": 2, "nodes": 3, "edges": 2, diff --git a/projectgraph/testdata/typescript/tsconfig-paths/graph.golden.json b/projectgraph/testdata/typescript/tsconfig-paths/graph.golden.json index 631dd1b2..109ccf31 100644 --- a/projectgraph/testdata/typescript/tsconfig-paths/graph.golden.json +++ b/projectgraph/testdata/typescript/tsconfig-paths/graph.golden.json @@ -138,7 +138,7 @@ "src/main.ts": "indexed" }, "stats": { - "version": "11", + "version": "12", "files": 3, "nodes": 5, "edges": 4, diff --git a/projectgraph/testdata/typescript/type-only/graph.golden.json b/projectgraph/testdata/typescript/type-only/graph.golden.json index 0e27da62..b7e756ff 100644 --- a/projectgraph/testdata/typescript/type-only/graph.golden.json +++ b/projectgraph/testdata/typescript/type-only/graph.golden.json @@ -97,7 +97,7 @@ "src/types.ts": "indexed" }, "stats": { - "version": "11", + "version": "12", "files": 3, "nodes": 5, "edges": 2, diff --git a/test/projectgraph/cases/go/chained-external-call/expect.yaml b/test/projectgraph/cases/go/chained-external-call/expect.yaml new file mode 100644 index 00000000..34bc0250 --- /dev/null +++ b/test/projectgraph/cases/go/chained-external-call/expect.yaml @@ -0,0 +1,68 @@ +source: safedep:hand +description: >- + run calls Next on the result of html.NewTokenizer, with no dependency code. The link step does not know the + type of that result, so the site keeps a record with the method Next and the reason unknown-receiver. The + target Tokenizer.Next is then uncertain at the site, not no-such-target. The edge to the key + html//NewTokenizer//Next stays, because a Go call of a type name converts a value, and a method of the type + then runs. +roots: + - key: main.go + kind: main + rule: go.main + - key: main.go//main + kind: main + rule: go.main + - key: tokens.go + kind: main + rule: go.main +exact_edges: true +edges: + - from: main.go//main + to: tokens.go//run + kind: direct + confidence: high + site: main.go:6 + - from: main.go//main + to: os//Stdin + kind: reference + confidence: low + site: main.go:6 + - from: tokens.go//run + to: golang.org//x//net//html//NewTokenizer + kind: direct + confidence: high + site: tokens.go:10 + - from: tokens.go//run + to: golang.org//x//net//html//NewTokenizer//Next + kind: direct + confidence: high + site: tokens.go:10 +unresolved: + - caller: tokens.go//run + site: tokens.go:10 + reason: unknown-receiver + method: Next +results: + - target: golang.org//x//net//html//Tokenizer//Next + status: uncertain + uncertainties: + - kind: unresolved-call + site: tokens.go:10 + - target: golang.org//x//net//html//NewTokenizer + status: found + path: + - key: main.go//main + - key: tokens.go//run + site: main.go:6 + kind: direct + confidence: high + - key: golang.org//x//net//html//NewTokenizer + site: tokens.go:10 + kind: direct + confidence: high + call_sites: + - caller: tokens.go//run + site: tokens.go:10 + reached: true + - target: golang.org//x//net//http2//Server//ServeConn + status: no-such-target diff --git a/test/projectgraph/cases/go/chained-external-call/project/go.mod b/test/projectgraph/cases/go/chained-external-call/project/go.mod new file mode 100644 index 00000000..af3f59c0 --- /dev/null +++ b/test/projectgraph/cases/go/chained-external-call/project/go.mod @@ -0,0 +1,5 @@ +module app + +go 1.22 + +require golang.org/x/net v0.61.0 diff --git a/test/projectgraph/cases/go/chained-external-call/project/main.go b/test/projectgraph/cases/go/chained-external-call/project/main.go new file mode 100644 index 00000000..eeb8f1fa --- /dev/null +++ b/test/projectgraph/cases/go/chained-external-call/project/main.go @@ -0,0 +1,7 @@ +package main + +import "os" + +func main() { + run(os.Stdin) +} diff --git a/test/projectgraph/cases/go/chained-external-call/project/tokens.go b/test/projectgraph/cases/go/chained-external-call/project/tokens.go new file mode 100644 index 00000000..fe62697c --- /dev/null +++ b/test/projectgraph/cases/go/chained-external-call/project/tokens.go @@ -0,0 +1,11 @@ +package main + +import ( + "io" + + "golang.org/x/net/html" +) + +func run(r io.Reader) { + html.NewTokenizer(r).Next() +} diff --git a/test/projectgraph/cases/go/dependency-implements-interface/summaries/go_github.com_acme_dep_v1.0.0.json b/test/projectgraph/cases/go/dependency-implements-interface/summaries/go_github.com_acme_dep_v1.0.0.json index bdce1cd7..8836d4ff 100644 --- a/test/projectgraph/cases/go/dependency-implements-interface/summaries/go_github.com_acme_dep_v1.0.0.json +++ b/test/projectgraph/cases/go/dependency-implements-interface/summaries/go_github.com_acme_dep_v1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "11", - "link_version": "11", + "link_version": "12", "unit": "go:github.com/acme/dep@v1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/go/dependency-module/summaries/go_github.com_acme_yamlish_v1.2.0.json b/test/projectgraph/cases/go/dependency-module/summaries/go_github.com_acme_yamlish_v1.2.0.json index f1621e24..1de719a9 100644 --- a/test/projectgraph/cases/go/dependency-module/summaries/go_github.com_acme_yamlish_v1.2.0.json +++ b/test/projectgraph/cases/go/dependency-module/summaries/go_github.com_acme_yamlish_v1.2.0.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "11", - "link_version": "11", + "link_version": "12", "unit": "go:github.com/acme/yamlish@v1.2.0", "symbols": [ { diff --git a/test/projectgraph/cases/go/interface-method-set/expect.yaml b/test/projectgraph/cases/go/interface-method-set/expect.yaml index 582b0847..654456c1 100644 --- a/test/projectgraph/cases/go/interface-method-set/expect.yaml +++ b/test/projectgraph/cases/go/interface-method-set/expect.yaml @@ -1,5 +1,5 @@ source: safedep:hand -description: start calls Run on a parameter of the interface type Runner, which declares Run and Stop. Real has both methods, so it can hold the call. Script has Run with the arity of the call but no Stop, so it is no Runner and its Run gets no cha edge. The site keeps a record with the method Run, and a definition named Run reaches exec.Command, so exec.Command is uncertain and not proved unreached. +description: start calls Run on a parameter of the interface type Runner, which declares Run and Stop. Real has both methods, so it can hold the call. Script has Run with the arity of the call but no Stop, so it is no Runner and its Run gets no cha edge. The site keeps a record with the method Run, and a definition named Run reaches exec.Command, so exec.Command is uncertain and not proved unreached. Script.Run calls Run on the result of exec.Command. The link step does not know the type of that result with no dependency code, so the site keeps a record. roots: - key: cmd/app/main.go kind: main @@ -21,6 +21,10 @@ unresolved: site: cmd/app/main.go:10 reason: no-definition method: Run + - caller: worker/script.go//Script//Run + site: worker/script.go:8 + reason: unknown-receiver + method: Run results: - target: net//http//Get status: found diff --git a/test/projectgraph/cases/javascript/commander-action/expect.yaml b/test/projectgraph/cases/javascript/commander-action/expect.yaml index 23019624..139f9e23 100644 --- a/test/projectgraph/cases/javascript/commander-action/expect.yaml +++ b/test/projectgraph/cases/javascript/commander-action/expect.yaml @@ -1,5 +1,5 @@ source: safedep:hand -description: program.command('convert ').action(convert) resolves to a key under commander, so convert is a root of the rule node.commander.action at the call. +description: program.command('convert ').action(convert) resolves to a key under commander, so convert is a root of the rule node.commander.action at the call. The call of action is a call on the result of command, whose type the link step does not know, so the site keeps a record. roots: - key: bin/cli.js//convert kind: framework @@ -11,7 +11,11 @@ edges: kind: direct confidence: high site: bin/cli.js:5 -unresolved: [] +unresolved: + - caller: bin/cli.js + site: bin/cli.js:8 + reason: unknown-receiver + method: action results: - target: js-yaml//load status: found diff --git a/test/projectgraph/cases/javascript/dependency-candidates/summaries/npm_store-kit_1.0.0.json b/test/projectgraph/cases/javascript/dependency-candidates/summaries/npm_store-kit_1.0.0.json index e9fbde93..7d682070 100644 --- a/test/projectgraph/cases/javascript/dependency-candidates/summaries/npm_store-kit_1.0.0.json +++ b/test/projectgraph/cases/javascript/dependency-candidates/summaries/npm_store-kit_1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "11", - "link_version": "11", + "link_version": "12", "unit": "npm:store-kit@1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/javascript/dependency-exports-import/summaries/npm_kit_1.0.0.json b/test/projectgraph/cases/javascript/dependency-exports-import/summaries/npm_kit_1.0.0.json index 740f3e2f..16f7e673 100644 --- a/test/projectgraph/cases/javascript/dependency-exports-import/summaries/npm_kit_1.0.0.json +++ b/test/projectgraph/cases/javascript/dependency-exports-import/summaries/npm_kit_1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "11", - "link_version": "11", + "link_version": "12", "unit": "npm:kit@1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/javascript/dependency-exports-require/summaries/npm_kit_1.0.0.json b/test/projectgraph/cases/javascript/dependency-exports-require/summaries/npm_kit_1.0.0.json index 740f3e2f..16f7e673 100644 --- a/test/projectgraph/cases/javascript/dependency-exports-require/summaries/npm_kit_1.0.0.json +++ b/test/projectgraph/cases/javascript/dependency-exports-require/summaries/npm_kit_1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "11", - "link_version": "11", + "link_version": "12", "unit": "npm:kit@1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/javascript/dependency-method-name/summaries/npm_plug_1.0.0.json b/test/projectgraph/cases/javascript/dependency-method-name/summaries/npm_plug_1.0.0.json index 81d96fc2..7c8c9b59 100644 --- a/test/projectgraph/cases/javascript/dependency-method-name/summaries/npm_plug_1.0.0.json +++ b/test/projectgraph/cases/javascript/dependency-method-name/summaries/npm_plug_1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "11", - "link_version": "11", + "link_version": "12", "unit": "npm:plug@1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/javascript/dependency-same-path/summaries/npm_util-kit_1.0.0.json b/test/projectgraph/cases/javascript/dependency-same-path/summaries/npm_util-kit_1.0.0.json index 9ad20a06..539bf522 100644 --- a/test/projectgraph/cases/javascript/dependency-same-path/summaries/npm_util-kit_1.0.0.json +++ b/test/projectgraph/cases/javascript/dependency-same-path/summaries/npm_util-kit_1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "11", - "link_version": "11", + "link_version": "12", "unit": "npm:util-kit@1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/javascript/dependency-transitive/summaries/npm_lodash._reinterpolate_3.0.0.json b/test/projectgraph/cases/javascript/dependency-transitive/summaries/npm_lodash._reinterpolate_3.0.0.json index 08756010..f7ddda0c 100644 --- a/test/projectgraph/cases/javascript/dependency-transitive/summaries/npm_lodash._reinterpolate_3.0.0.json +++ b/test/projectgraph/cases/javascript/dependency-transitive/summaries/npm_lodash._reinterpolate_3.0.0.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "11", - "link_version": "11", + "link_version": "12", "unit": "npm:lodash._reinterpolate@3.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/javascript/dependency-transitive/summaries/npm_lodash_4.17.21.json b/test/projectgraph/cases/javascript/dependency-transitive/summaries/npm_lodash_4.17.21.json index 80b848c8..4ac7ee48 100644 --- a/test/projectgraph/cases/javascript/dependency-transitive/summaries/npm_lodash_4.17.21.json +++ b/test/projectgraph/cases/javascript/dependency-transitive/summaries/npm_lodash_4.17.21.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "11", - "link_version": "11", + "link_version": "12", "unit": "npm:lodash@4.17.21", "symbols": [ { diff --git a/test/projectgraph/cases/javascript/references-argument/expect.yaml b/test/projectgraph/cases/javascript/references-argument/expect.yaml index 00f048e5..15ec72d2 100644 --- a/test/projectgraph/cases/javascript/references-argument/expect.yaml +++ b/test/projectgraph/cases/javascript/references-argument/expect.yaml @@ -1,5 +1,5 @@ source: safedep:hand -description: src/main.js passes the imported load to Array.map and the first-party parse to Promise.then. Each reference makes its function possibly called. +description: src/main.js passes the imported load to Array.map and the first-party parse to Promise.then. Each reference makes its function possibly called. The call of then is a call on the result of Promise.resolve, whose type the link step does not know, so the site keeps a record. roots: - key: src/main.js kind: manifest @@ -15,7 +15,11 @@ edges: kind: reference confidence: low site: src/main.js:5 -unresolved: [] +unresolved: + - caller: src/main.js + site: src/main.js:5 + reason: unknown-receiver + method: then results: - target: js-yaml//load status: found diff --git a/test/projectgraph/cases/python/dependency-dark-unit/summaries/pypi_PyYAML_6.0.1.json b/test/projectgraph/cases/python/dependency-dark-unit/summaries/pypi_PyYAML_6.0.1.json index aa139f6b..61db2c73 100644 --- a/test/projectgraph/cases/python/dependency-dark-unit/summaries/pypi_PyYAML_6.0.1.json +++ b/test/projectgraph/cases/python/dependency-dark-unit/summaries/pypi_PyYAML_6.0.1.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "11", - "link_version": "11", + "link_version": "12", "unit": "pypi:PyYAML@6.0.1", "symbols": [ { diff --git a/test/projectgraph/cases/python/dependency-failed-reached/summaries/pypi_badpkg_1.0.0.json b/test/projectgraph/cases/python/dependency-failed-reached/summaries/pypi_badpkg_1.0.0.json index ba9a9dff..8d4b6622 100644 --- a/test/projectgraph/cases/python/dependency-failed-reached/summaries/pypi_badpkg_1.0.0.json +++ b/test/projectgraph/cases/python/dependency-failed-reached/summaries/pypi_badpkg_1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "11", - "link_version": "11", + "link_version": "12", "unit": "pypi:badpkg@1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/python/dependency-failed-reached/summaries/pypi_okpkg_1.0.0.json b/test/projectgraph/cases/python/dependency-failed-reached/summaries/pypi_okpkg_1.0.0.json index e1ded119..fb663fca 100644 --- a/test/projectgraph/cases/python/dependency-failed-reached/summaries/pypi_okpkg_1.0.0.json +++ b/test/projectgraph/cases/python/dependency-failed-reached/summaries/pypi_okpkg_1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "11", - "link_version": "11", + "link_version": "12", "unit": "pypi:okpkg@1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/python/dependency-failed-unreached/summaries/pypi_badpkg_1.0.0.json b/test/projectgraph/cases/python/dependency-failed-unreached/summaries/pypi_badpkg_1.0.0.json index ba9a9dff..8d4b6622 100644 --- a/test/projectgraph/cases/python/dependency-failed-unreached/summaries/pypi_badpkg_1.0.0.json +++ b/test/projectgraph/cases/python/dependency-failed-unreached/summaries/pypi_badpkg_1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "11", - "link_version": "11", + "link_version": "12", "unit": "pypi:badpkg@1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/python/dependency-failed-unreached/summaries/pypi_okpkg_1.0.0.json b/test/projectgraph/cases/python/dependency-failed-unreached/summaries/pypi_okpkg_1.0.0.json index e1ded119..fb663fca 100644 --- a/test/projectgraph/cases/python/dependency-failed-unreached/summaries/pypi_okpkg_1.0.0.json +++ b/test/projectgraph/cases/python/dependency-failed-unreached/summaries/pypi_okpkg_1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "11", - "link_version": "11", + "link_version": "12", "unit": "pypi:okpkg@1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/python/dependency-package-dir/summaries/pypi_outer_1.0.0.json b/test/projectgraph/cases/python/dependency-package-dir/summaries/pypi_outer_1.0.0.json index 67b3ee37..53b1a049 100644 --- a/test/projectgraph/cases/python/dependency-package-dir/summaries/pypi_outer_1.0.0.json +++ b/test/projectgraph/cases/python/dependency-package-dir/summaries/pypi_outer_1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "11", - "link_version": "11", + "link_version": "12", "unit": "pypi:outer@1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/python/dependency-site-packages/summaries/pypi_requests_2.31.0.json b/test/projectgraph/cases/python/dependency-site-packages/summaries/pypi_requests_2.31.0.json index 333c730a..5fb351aa 100644 --- a/test/projectgraph/cases/python/dependency-site-packages/summaries/pypi_requests_2.31.0.json +++ b/test/projectgraph/cases/python/dependency-site-packages/summaries/pypi_requests_2.31.0.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "11", - "link_version": "11", + "link_version": "12", "unit": "pypi:requests@2.31.0", "symbols": [ { diff --git a/test/projectgraph/cases/python/dependency-site-packages/summaries/pypi_urllib3_2.0.7.json b/test/projectgraph/cases/python/dependency-site-packages/summaries/pypi_urllib3_2.0.7.json index e7b25688..29327fa3 100644 --- a/test/projectgraph/cases/python/dependency-site-packages/summaries/pypi_urllib3_2.0.7.json +++ b/test/projectgraph/cases/python/dependency-site-packages/summaries/pypi_urllib3_2.0.7.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "11", - "link_version": "11", + "link_version": "12", "unit": "pypi:urllib3@2.0.7", "symbols": [ { diff --git a/test/projectgraph/receivers_test.go b/test/projectgraph/receivers_test.go index 3b7fc6c9..cb89dd06 100644 --- a/test/projectgraph/receivers_test.go +++ b/test/projectgraph/receivers_test.go @@ -92,16 +92,16 @@ var goReceiverRows = []receiverRow{ source: "func run() {\n\tdep.NewThing().Do()\n}", target: "example.com//dep//Thing//Do", want: "found", - gap: "FOU-546", - today: "no-such-target", + gap: "FOU-543", + today: "uncertain/unknown-receiver", }, { name: "chained method call", source: "func run() {\n\tdep.NewThing().Next().Do()\n}", target: "example.com//dep//Thing//Do", want: "found", - gap: "FOU-546", - today: "no-such-target", + gap: "FOU-543", + today: "uncertain/unknown-receiver", }, { name: "interface return", diff --git a/test/projectgraph/version_test.go b/test/projectgraph/version_test.go index 2f2fe985..7b920dae 100644 --- a/test/projectgraph/version_test.go +++ b/test/projectgraph/version_test.go @@ -25,6 +25,7 @@ var graphHashes = map[string]string{ "10/10": "fa32ae11c72bdf01c93d4b7afe3564b1d59b2436306e3b3503f01fc4c308e5aa", "11/10": "421c6441093c8f82e4db8a2342884b5e42faf360951985d3600d99c96202625b", "11/11": "19e0527969cf2702f5abfab8234310b254ac25a6a539ab6e86cf6febabfb8757", + "11/12": "14d5bbbaca07ab950f75c665f48e71ce9195da2ebd853cd1b0b745d42c4852cc", } // resultHashes records the hash of the reach results of every case for @@ -34,6 +35,7 @@ var resultHashes = map[string]string{ "11/10/1": "4beb6248ef1b8dc3572939a41d1aea11c6504f5007a5f24fd1261db026708186", "11/11/1": "4beb6248ef1b8dc3572939a41d1aea11c6504f5007a5f24fd1261db026708186", "11/11/2": "58d8fd9c5b67bf02a44778a5173828515c9424a1fddd9ff3f644d8fc13ebcfbb", + "11/12/2": "8341f2a1965f9d3b19d976ff1ba63ed26c1e15026e02079ec6195dfc0d762dba", } // summaryHashes records the hash of the golden summaries of the cases for @@ -44,6 +46,7 @@ var summaryHashes = map[string]string{ "11/10/1": "f3c6c7e7f2390a0c8ec81ab6673b6b8a02cadf95e0ca36dc963d57b72834589c", "11/10/2": "8047cc4e4d95ce6744993a20c7f4224a78b2d3531c2e31415f371b16608f19a5", "11/11/2": "c0aeb7f515b2c546fd6b355663d5a7bb446c7f6bdfb9ab3ef343dd252e4302bf", + "11/12/2": "f4102bd40a876fa387f7ff1ce81505e1f04b64cd8165602d0726f897e2b88a7f", } func TestGraphHashFollowsVersion(t *testing.T) { From 691e4104d885d7677f2e952c74f53c6ba7f93883 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 19 Oct 2026 07:48:56 +0000 Subject: [PATCH 4/9] fix(indexer): leave out Go files that no build reads and blank only the data of tables (FOU-547) A -deps run with golang.org/x/net@v0.61.0 printed seven truncations of the package-clauses limit, a budget line for idna/tables15.0.0.go and unresolved calls of _test.go files. Each gen.go has //go:build ignore and package main, so the link step saw a second package clause in its directory. The table file hit the depth limit of the index with a string of thousands of parts. Each truncation and budget turned a reach answer for the module into uncertain, so the dependency code made the answer worse. The index step now leaves out a Go file of a dependency unit only when no Go build reads it by default: a _test.go file, a file with //go:build ignore, and a file whose constraint needs a tag that the go command never sets, as purego. go/build decides for each GOOS and GOARCH with the bytes that the run read. The index keeps the files of every platform, because a reach answer must hold for each platform that a project builds for. An index of one platform dropped the darwin twin of a function, and a call that only darwin makes read as no-such-target. Result.Excluded lists the files. A file of the project stays as it is. A generated Go file of a unit, or a tables*.go file, whose index hits a limit gets a second index from its skeleton: each top-level var value that holds only data blanked, with the lines and the columns kept. Function bodies and values with a call or a function literal stay, so the calls of a generated file stay in the graph. A skeleton without bodies dropped every call of a .pb.go file and marked it complete. The index keeps the hash of the file, so the store serves it on the next run. A skeleton that still hits a limit stays truncated. The same x/net run now prints no truncation and no budget line. An indexer test checks a unit with gen.go, a _test.go file, a purego file, a //go:build windows file, a _linux.go file and a large generated table whose body calls a function. The cases go/dependency-build-files, go/dependency-generated-code and go/dependency-platform-files check the graph and the reach answers. fileindex.Version is 12. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01PFQbqPqd9xn1fT6hBsyoUm Signed-off-by: Claude --- docs/projectgraph.md | 16 ++ fileindex/index.go | 2 +- fileindex/testdata/index.json | 2 +- plugin/indexer/gounit.go | 148 ++++++++++++++++++ plugin/indexer/gounit_test.go | 99 ++++++++++++ plugin/indexer/indexer.go | 18 ++- plugin/indexer/indexer_pool.go | 3 + plugin/indexer/plugin_test.go | 1 + plugin/indexer/testdata/golden/App.cs.json | 2 +- plugin/indexer/testdata/golden/App.java.json | 2 +- plugin/indexer/testdata/golden/App.kt.json | 2 +- plugin/indexer/testdata/golden/app.go.json | 2 +- plugin/indexer/testdata/golden/app.js.json | 2 +- plugin/indexer/testdata/golden/app.php.json | 2 +- plugin/indexer/testdata/golden/app.py.json | 2 +- plugin/indexer/testdata/golden/app.rb.json | 2 +- plugin/indexer/testdata/golden/app.rs.json | 2 +- plugin/indexer/testdata/golden/app.ts.json | 2 +- .../indexer/testdata/golden/exports.js.json | 2 +- .../indexer/testdata/golden/exports.py.json | 2 +- .../indexer/testdata/golden/exports.ts.json | 2 +- plugin/indexer/testdata/golden/flags.js.json | 2 +- plugin/indexer/testdata/golden/flags.py.json | 2 +- plugin/indexer/testdata/golden/flags.ts.json | 2 +- .../javascript/barrel-chain/indexes.json | 8 +- .../javascript/commonjs-require/indexes.json | 6 +- .../javascript/default-import/indexes.json | 6 +- .../javascript/esm-named-import/indexes.json | 6 +- .../javascript/export-star/indexes.json | 10 +- .../exports-conditions/indexes.json | 8 +- .../javascript/unknown-receiver/indexes.json | 2 +- .../javascript/workspaces/indexes.json | 6 +- .../testdata/python/all-hides/indexes.json | 6 +- .../testdata/python/assignment/indexes.json | 2 +- .../testdata/python/class-body/indexes.json | 2 +- .../testdata/python/decorators/indexes.json | 2 +- .../testdata/python/external/indexes.json | 2 +- .../python/local-receiver/indexes.json | 2 +- .../testdata/python/member-call/indexes.json | 6 +- .../testdata/python/missing-file/indexes.json | 2 +- .../testdata/python/package-init/indexes.json | 6 +- .../python/reexport-cycle/indexes.json | 6 +- .../python/relative-imports/indexes.json | 6 +- .../python/sibling-import/indexes.json | 4 +- .../testdata/python/src-layout/indexes.json | 6 +- .../testdata/python/sys-path/indexes.json | 4 +- .../testdata/python/wildcard/indexes.json | 4 +- .../testdata/roots/extra-rules/indexes.json | 6 +- .../roots/library-surface/indexes.json | 4 +- .../testdata/roots/main-guard/indexes.json | 4 +- .../roots/package-json-bin/indexes.json | 4 +- .../roots/package-json-main/indexes.json | 8 +- .../roots/procfile-dockerfile/indexes.json | 12 +- .../roots/pyproject-scripts/indexes.json | 10 +- .../testdata/roots/setup-py/indexes.json | 4 +- .../testdata/roots/test-file/indexes.json | 2 +- .../typescript/js-specifier/indexes.json | 4 +- .../typescript/tsconfig-paths/indexes.json | 6 +- .../typescript/type-only/indexes.json | 6 +- .../go/dependency-build-files/expect.yaml | 87 ++++++++++ .../go/dependency-build-files/project/go.mod | 5 + .../gomod/github.com/acme/kit@v1.0.0/gen.go | 9 ++ .../gomod/github.com/acme/kit@v1.0.0/go.mod | 3 + .../gomod/github.com/acme/kit@v1.0.0/kit.go | 8 + .../github.com/acme/kit@v1.0.0/kit_test.go | 11 ++ .../github.com/acme/kit@v1.0.0/kit_windows.go | 9 ++ .../github.com/acme/kit@v1.0.0/tables.go | 9 ++ .../go/dependency-build-files/project/main.go | 7 + .../go_github.com_acme_kit_v1.0.0.json | 30 ++++ .../go/dependency-generated-code/expect.yaml | 41 +++++ .../dependency-generated-code/project/go.mod | 5 + .../gomod/github.com/acme/kit@v1.0.0/gen.go | 9 ++ .../gomod/github.com/acme/kit@v1.0.0/go.mod | 3 + .../gomod/github.com/acme/kit@v1.0.0/kit.go | 3 + .../dependency-generated-code/project/main.go | 7 + .../go_github.com_acme_kit_v1.0.0.json | 20 +++ .../go_github.com_acme_dep_v1.0.0.json | 2 +- .../go_github.com_acme_yamlish_v1.2.0.json | 2 +- .../go/dependency-platform-files/expect.yaml | 40 +++++ .../dependency-platform-files/project/go.mod | 5 + .../gomod/github.com/acme/kit@v1.0.0/go.mod | 3 + .../gomod/github.com/acme/kit@v1.0.0/kit.go | 3 + .../github.com/acme/kit@v1.0.0/kit_darwin.go | 5 + .../github.com/acme/kit@v1.0.0/kit_linux.go | 3 + .../dependency-platform-files/project/main.go | 7 + .../go_github.com_acme_kit_v1.0.0.json | 20 +++ .../summaries/npm_store-kit_1.0.0.json | 2 +- .../summaries/npm_kit_1.0.0.json | 2 +- .../summaries/npm_kit_1.0.0.json | 2 +- .../summaries/npm_plug_1.0.0.json | 2 +- .../summaries/npm_util-kit_1.0.0.json | 2 +- .../npm_lodash._reinterpolate_3.0.0.json | 2 +- .../summaries/npm_lodash_4.17.21.json | 2 +- .../summaries/pypi_PyYAML_6.0.1.json | 2 +- .../summaries/pypi_badpkg_1.0.0.json | 2 +- .../summaries/pypi_okpkg_1.0.0.json | 2 +- .../summaries/pypi_badpkg_1.0.0.json | 2 +- .../summaries/pypi_okpkg_1.0.0.json | 2 +- .../summaries/pypi_outer_1.0.0.json | 2 +- .../summaries/pypi_requests_2.31.0.json | 2 +- .../summaries/pypi_urllib3_2.0.7.json | 2 +- test/projectgraph/version_test.go | 3 + 102 files changed, 764 insertions(+), 128 deletions(-) create mode 100644 plugin/indexer/gounit.go create mode 100644 plugin/indexer/gounit_test.go create mode 100644 test/projectgraph/cases/go/dependency-build-files/expect.yaml create mode 100644 test/projectgraph/cases/go/dependency-build-files/project/go.mod create mode 100644 test/projectgraph/cases/go/dependency-build-files/project/gomod/github.com/acme/kit@v1.0.0/gen.go create mode 100644 test/projectgraph/cases/go/dependency-build-files/project/gomod/github.com/acme/kit@v1.0.0/go.mod create mode 100644 test/projectgraph/cases/go/dependency-build-files/project/gomod/github.com/acme/kit@v1.0.0/kit.go create mode 100644 test/projectgraph/cases/go/dependency-build-files/project/gomod/github.com/acme/kit@v1.0.0/kit_test.go create mode 100644 test/projectgraph/cases/go/dependency-build-files/project/gomod/github.com/acme/kit@v1.0.0/kit_windows.go create mode 100644 test/projectgraph/cases/go/dependency-build-files/project/gomod/github.com/acme/kit@v1.0.0/tables.go create mode 100644 test/projectgraph/cases/go/dependency-build-files/project/main.go create mode 100644 test/projectgraph/cases/go/dependency-build-files/summaries/go_github.com_acme_kit_v1.0.0.json create mode 100644 test/projectgraph/cases/go/dependency-generated-code/expect.yaml create mode 100644 test/projectgraph/cases/go/dependency-generated-code/project/go.mod create mode 100644 test/projectgraph/cases/go/dependency-generated-code/project/gomod/github.com/acme/kit@v1.0.0/gen.go create mode 100644 test/projectgraph/cases/go/dependency-generated-code/project/gomod/github.com/acme/kit@v1.0.0/go.mod create mode 100644 test/projectgraph/cases/go/dependency-generated-code/project/gomod/github.com/acme/kit@v1.0.0/kit.go create mode 100644 test/projectgraph/cases/go/dependency-generated-code/project/main.go create mode 100644 test/projectgraph/cases/go/dependency-generated-code/summaries/go_github.com_acme_kit_v1.0.0.json create mode 100644 test/projectgraph/cases/go/dependency-platform-files/expect.yaml create mode 100644 test/projectgraph/cases/go/dependency-platform-files/project/go.mod create mode 100644 test/projectgraph/cases/go/dependency-platform-files/project/gomod/github.com/acme/kit@v1.0.0/go.mod create mode 100644 test/projectgraph/cases/go/dependency-platform-files/project/gomod/github.com/acme/kit@v1.0.0/kit.go create mode 100644 test/projectgraph/cases/go/dependency-platform-files/project/gomod/github.com/acme/kit@v1.0.0/kit_darwin.go create mode 100644 test/projectgraph/cases/go/dependency-platform-files/project/gomod/github.com/acme/kit@v1.0.0/kit_linux.go create mode 100644 test/projectgraph/cases/go/dependency-platform-files/project/main.go create mode 100644 test/projectgraph/cases/go/dependency-platform-files/summaries/go_github.com_acme_kit_v1.0.0.json diff --git a/docs/projectgraph.md b/docs/projectgraph.md index ecdd431b..69097f2c 100644 --- a/docs/projectgraph.md +++ b/docs/projectgraph.md @@ -63,6 +63,22 @@ and no source text past a short call text. files of dependency code, and the report of the run. `Result.Unit` returns the indexes and the files of one unit of dependency code. +A generated Go file of a unit, with a `// Code generated ... DO NOT EDIT.` +line or a name that starts with `tables`, can hit a limit of the index, +as a table of thousands of entries. Its index then comes from the file +with each top-level var value that holds only data blanked. Function bodies +and values with a call or a function literal stay, so the calls of a +generated file, as a `.pb.go` file, stay in the graph. The index keeps the +hash of the file. If the skeleton still hits a limit, the file stays +truncated, and reach counts it as budget. + +The index leaves out a Go file of a unit only when no Go build reads it by +default: a `_test.go` file, a file with `//go:build ignore`, and a file +whose constraint needs a tag that the go command never sets, as `purego`. +It keeps the files of every GOOS and GOARCH, so a reach answer holds for +each platform that a project builds for. `Result.Excluded` lists the files +that it leaves out. + When the context ends, `Run` stops, marks each file that it did not index as `missing` and returns the result so far with the error of the context. The link step takes the partial result, and the reach query reports each diff --git a/fileindex/index.go b/fileindex/index.go index e5ead0bc..f7914868 100644 --- a/fileindex/index.go +++ b/fileindex/index.go @@ -19,7 +19,7 @@ import ( // Version is the version of the index records and of the rules that fill // them. Bump it when a field, a kind, an order or a fill rule changes. A // store keys an index by Version, so the bump makes every older index miss. -const Version = "11" +const Version = "12" // DefaultTestPaths are the globs that mark the test files of the common // layouts, as projectgraph.Options.TestPaths takes them. A glob with no "/" diff --git a/fileindex/testdata/index.json b/fileindex/testdata/index.json index a08c6158..d5972d33 100644 --- a/fileindex/testdata/index.json +++ b/fileindex/testdata/index.json @@ -1,5 +1,5 @@ { - "version": "11", + "version": "12", "path": "src/app.py", "language": "python", "content_hash": "a172cedcae47474b615c54d510a5d84a8dea3032e958587430b413538be3f333", diff --git a/plugin/indexer/gounit.go b/plugin/indexer/gounit.go new file mode 100644 index 00000000..0af0b5ab --- /dev/null +++ b/plugin/indexer/gounit.go @@ -0,0 +1,148 @@ +package indexer + +import ( + "bytes" + "context" + "go/build" + "io" + "path" + "regexp" + "slices" + "strings" + + "github.com/safedep/code/core" + "github.com/safedep/code/fileindex" + "github.com/safedep/code/langcode" + "github.com/safedep/code/pkg/ts" + sitter "github.com/smacker/go-tree-sitter" +) + +// goOS and goArch are the GOOS and GOARCH values of the Go toolchain. A +// reach answer must hold for every platform that a project builds for, so +// the index keeps a file of a dependency unit when the build of any pair +// reads it. An index of one platform drops the other twin of a +// thing_linux.go and thing_darwin.go pair, and a call that only the darwin +// build makes then reads as no-such-target on a darwin host. +var ( + goOS = []string{"aix", "android", "darwin", "dragonfly", "freebsd", "hurd", "illumos", "ios", "js", "linux", "netbsd", "openbsd", "plan9", "solaris", "wasip1", "windows", "zos"} + goArch = []string{"386", "amd64", "arm", "arm64", "loong64", "mips", "mipsle", "mips64", "mips64le", "ppc64", "ppc64le", "riscv64", "s390x", "sparc64", "wasm"} +) + +// goExcluded reports whether no Go build reads a Go file of a dependency +// unit: a test file, a file with //go:build ignore, or a file whose +// constraint needs a tag that the go command never sets by itself, as +// purego. Such a file holds no code that an importer runs by default. Its +// package clause can differ from the clause of its directory, as package +// main of a gen.go, and its index would hold calls that no program makes. +// A build constraint that the build cannot read leaves the file in, so the +// index loses no code. +func goExcluded(file core.File, data []byte) bool { + if !file.IsImport() || path.Ext(file.Name()) != ".go" { + return false + } + name := path.Base(file.Name()) + if strings.HasSuffix(name, "_test.go") { + return true + } + ctxt := build.Default + ctxt.CgoEnabled = true + ctxt.BuildTags, ctxt.ToolTags = nil, nil + ctxt.OpenFile = func(string) (io.ReadCloser, error) { return io.NopCloser(bytes.NewReader(data)), nil } + for _, os := range goOS { + for _, arch := range goArch { + ctxt.GOOS, ctxt.GOARCH = os, arch + if match, err := ctxt.MatchFile(".", name); err != nil || match { + return false + } + } + } + return true +} + +// generatedHeader matches the line that marks a generated Go file. +var generatedHeader = regexp.MustCompile(`(?m)^// Code generated .* DO NOT EDIT\.$`) + +// isGeneratedTable reports whether a Go file of a dependency unit is a +// generated file or a table file, as idna/tables15.0.0.go of x/net. +func isGeneratedTable(name string, data []byte) bool { + if unit, _ := fileindex.SplitUnit(name); unit == "" || path.Ext(name) != ".go" { + return false + } + if base := path.Base(name); strings.HasPrefix(base, "tables") { + return true + } + head := data + if i := bytes.Index(data, []byte("\npackage ")); i >= 0 { + head = data[:i] + } + return generatedHeader.Match(head) +} + +// goSkeleton returns the bytes of a Go file with each value of a +// top-level var that holds only data replaced by 0 and spaces. The +// declarations keep their lines and columns, and the file still parses. The +// data values of a generated table file are what hit the limits of the +// index, as a string of thousands of concatenated parts. Function bodies and +// any value with a call or a function literal stay, because a generated +// file can hold code that a program runs, as a .pb.go file. +func goSkeleton(tree *sitter.Tree, data []byte) []byte { + out := bytes.Clone(data) + clear := func(from, to uint32) { + for i := from; i < to; i++ { + if out[i] != '\n' { + out[i] = ' ' + } + } + } + var visit func(node *sitter.Node) + visit = func(node *sitter.Node) { + switch node.Type() { + case "function_declaration", "method_declaration": + return + case "var_spec": + if values := node.ChildByFieldName("value"); values != nil { + for _, v := range ts.NamedChildren(values) { + if !holdsCode(v) { + clear(v.StartByte(), v.EndByte()) + out[v.StartByte()] = '0' + } + } + } + return + } + for _, child := range ts.NamedChildren(node) { + visit(child) + } + } + visit(tree.RootNode()) + return out +} + +// holdsCode reports whether an expression holds a call or a function +// literal, which a program runs when it initializes the package. +func holdsCode(node *sitter.Node) bool { + if t := node.Type(); t == "call_expression" || t == "func_literal" { + return true + } + return slices.ContainsFunc(ts.NamedChildren(node), holdsCode) +} + +// skeletonIndex indexes the skeleton of a generated table file of a +// dependency unit whose index hit a limit. The index keeps the hash of the +// bytes of the file, so the store serves it on the next run. +func skeletonIndex(ctx context.Context, p contentParser, file pending, tree core.ParseTree, ix *fileindex.Index) (*fileindex.Index, error) { + if ix == nil || ix.Language != langcode.Go || len(ix.Truncated) == 0 || partial(ix) || !isGeneratedTable(file.file.Name(), file.data) { + return ix, nil + } + skeleton, err := p.ParseContent(ctx, file.file, goSkeleton(tree.Tree(), file.data)) + if err != nil { + return nil, err + } + defer skeleton.Close() + out, err := project(ctx, skeleton) + if err != nil { + return nil, err + } + out.ContentHash = file.key.ContentHash + return out, nil +} diff --git a/plugin/indexer/gounit_test.go b/plugin/indexer/gounit_test.go new file mode 100644 index 00000000..02432a8e --- /dev/null +++ b/plugin/indexer/gounit_test.go @@ -0,0 +1,99 @@ +package indexer + +import ( + "context" + "path/filepath" + "regexp" + "strings" + "testing" + + "github.com/safedep/code/core" + "github.com/safedep/code/fileindex" + "github.com/safedep/code/fileindex/store/memstore" + "github.com/safedep/code/fs" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +const goKitUnit = "go:example.com/kit@v1.0.0" + +// goKitFiles is a Go module with a file of each kind that the index keeps +// or leaves out. tables.go is a generated table file whose var holds a +// string of 60 parts, which nests past the depth limit of the index. Its +// Lookup calls helper, and its second var calls helper too. +func goKitFiles() map[string]string { + parts := strings.Repeat(` + "x"`, 60) + return map[string]string{ + "go.mod": "module example.com/kit\n\ngo 1.22\n", + "kit.go": "package kit\n\nfunc Run() { helper() }\n\nfunc helper() {}\n", + "kit_test.go": "package kit\n\nimport \"testing\"\n\nfunc TestRun(t *testing.T) { Run() }\n", + "kit_windows.go": "//go:build windows\n\npackage kit\n\nfunc OnlyWindows() {}\n", + "kit_linux.go": "package kit\n\nfunc OnlyLinux() {}\n", + "kit_purego.go": "//go:build purego\n\npackage kit\n\nfunc PureGo() {}\n", + "gen.go": "//go:build ignore\n\npackage main\n\nfunc main() {}\n", + "tables.go": "// Code generated by synth. DO NOT EDIT.\n\npackage kit\n\nfunc Lookup() int { helper(); return len(data) }\n\nvar data string = \"\"" + parts + "\n\nvar ready = helper\n", + } +} + +func TestIndexerReadsTheGoBuildsOfADependencyUnit(t *testing.T) { + dir := t.TempDir() + writeFiles(t, dir, map[string]string{"main.go": "package main\n\nfunc main() {}\n", "main_test.go": "package main\n"}) + kit := filepath.Join(dir, "vendor", "kit") + writeFiles(t, kit, goKitFiles()) + fsys, err := fs.NewLocalFileSystem(fs.LocalFileSystemConfig{ + AppDirectories: []string{dir}, + ImportDirectories: []string{kit}, + ImportUnits: map[string]string{kit: goKitUnit}, + ExcludePatterns: []*regexp.Regexp{fs.SkipPattern(kit)}, + }) + require.NoError(t, err) + result, err := runIndexer(t, context.Background(), fsys, Options{Languages: languages(t, core.LanguageCodeGo), Store: memstore.New(), Imports: true}) + require.NoError(t, err) + + var indexed, excluded []string + for p, status := range result.Dependencies { + _, inner := fileindex.SplitUnit(p) + assert.Equal(t, fileindex.FileIndexed, status, p) + indexed = append(indexed, inner) + } + for _, p := range result.Excluded { + _, inner := fileindex.SplitUnit(p) + excluded = append(excluded, inner) + } + assert.ElementsMatch(t, []string{"kit.go", "kit_linux.go", "kit_windows.go", "tables.go"}, indexed, "a file that any platform builds stays") + assert.ElementsMatch(t, []string{"gen.go", "kit_purego.go", "kit_test.go"}, excluded) + assert.Equal(t, map[string]fileindex.FileStatus{"main.go": fileindex.FileIndexed, "main_test.go": fileindex.FileIndexed}, result.Files, "a test file of the project stays") + + var tables *fileindex.Index + for _, ix := range result.Indexes { + if ix.Path == goKitUnit+"//tables.go" { + tables = ix + } + } + require.NotNil(t, tables) + assert.Empty(t, tables.Truncated, "the skeleton of the table file hits no limit") + var keys, callees []string + for _, def := range tables.Definitions { + keys = append(keys, def.Key) + } + for _, call := range tables.Calls { + callees = append(callees, call.Callee) + } + assert.Contains(t, keys, goKitUnit+"//tables.go//Lookup") + assert.Contains(t, callees, "helper", "the skeleton keeps the bodies") +} + +func TestGoSkeletonKeepsTheHashOfTheFile(t *testing.T) { + dir := t.TempDir() + kit := filepath.Join(dir, "kit") + writeFiles(t, kit, goKitFiles()) + fsys, err := fs.NewLocalFileSystem(fs.LocalFileSystemConfig{AppDirectories: []string{dir}, ImportDirectories: []string{kit}, ImportUnits: map[string]string{kit: goKitUnit}, ExcludePatterns: []*regexp.Regexp{fs.SkipPattern(kit)}}) + require.NoError(t, err) + s := memstore.New() + opts := Options{Languages: languages(t, core.LanguageCodeGo), Store: s, Imports: true} + _, err = runIndexer(t, context.Background(), fsys, opts) + require.NoError(t, err) + result, err := runIndexer(t, context.Background(), fsys, opts) + require.NoError(t, err) + assert.Equal(t, fileindex.FileCached, result.Dependencies[goKitUnit+"//tables.go"], "the store serves the skeleton index on the next run") +} diff --git a/plugin/indexer/indexer.go b/plugin/indexer/indexer.go index 562acffd..58fdcccd 100644 --- a/plugin/indexer/indexer.go +++ b/plugin/indexer/indexer.go @@ -79,6 +79,12 @@ type Result struct { // Report lists the files that the run parsed, skipped and failed. Report *plugin.Report + + // Excluded lists the Go files of dependency code that no Go build + // reads by default, in walk order: test files, files with //go:build + // ignore and files that need a tag the go command never sets. They have + // no status, as no program runs their code. + Excluded []string } // Statuses returns the status of each file of the run, of the project and @@ -166,6 +172,8 @@ type run struct { result *Result pool *pool strs interner + // excluded holds the names of Result.Excluded. + excluded map[string]bool } // Run indexes the app files of fsys, and its import files with @@ -190,7 +198,8 @@ func (ix *Indexer) Run(ctx context.Context, fsys core.ImportAwareFileSystem) (*R } r := &run{ - opts: ix.opts, + opts: ix.opts, + excluded: map[string]bool{}, result: &Result{ Files: make(map[string]fileindex.FileStatus, len(files)), Dependencies: map[string]fileindex.FileStatus{}, @@ -206,7 +215,7 @@ func (ix *Indexer) Run(ctx context.Context, fsys core.ImportAwareFileSystem) (*R for _, file := range files { statuses := r.result.statuses(file.Name()) _, done := statuses[file.Name()] - if !done && !slices.Contains(r.result.Report.Skipped, file.Name()) { + if !done && !r.excluded[file.Name()] && !slices.Contains(r.result.Report.Skipped, file.Name()) { statuses[file.Name()] = fileindex.FileMissing } } @@ -265,6 +274,11 @@ func (r *run) index(ctx context.Context, files []core.File, byExtension map[stri r.fail(ctx, file, core.StageRead, err) continue } + if goExcluded(file, data) { + r.excluded[file.Name()] = true + r.result.Excluded = append(r.result.Excluded, file.Name()) + continue + } batch = append(batch, pending{ file: file, diff --git a/plugin/indexer/indexer_pool.go b/plugin/indexer/indexer_pool.go index f0982de6..40797ffb 100644 --- a/plugin/indexer/indexer_pool.go +++ b/plugin/indexer/indexer_pool.go @@ -122,5 +122,8 @@ func analyze(ctx context.Context, p contentParser, file pending) outcome { return outcome{tree: tree} } err = plugin.Analyze(ctx, indexPlugin, file.file, tree) + if err == nil { + index, err = skeletonIndex(ctx, p, file, tree, index) + } return outcome{tree: tree, index: index, indexErr: err, stage: core.Stage(indexPlugin.Name())} } diff --git a/plugin/indexer/plugin_test.go b/plugin/indexer/plugin_test.go index b920e4c6..4aa19c88 100644 --- a/plugin/indexer/plugin_test.go +++ b/plugin/indexer/plugin_test.go @@ -100,6 +100,7 @@ func TestFileIndexGolden(t *testing.T) { var goldenHashes = map[string]string{ "10": "6053fe7c2b8fef2888806debc47372948eec366beb800419c1e435f021711fa0", "11": "141e6d6a6f6d369635bf4f6781033acc269e59fe5f722558192b8e952c7e7bd9", + "12": "974943df4185f9c3070dcfdfedff8e91912e257d255a936f7c27608bb63a69f0", } func TestFileIndexGoldenHashFollowsVersion(t *testing.T) { diff --git a/plugin/indexer/testdata/golden/App.cs.json b/plugin/indexer/testdata/golden/App.cs.json index 3d97a199..9d30a79e 100644 --- a/plugin/indexer/testdata/golden/App.cs.json +++ b/plugin/indexer/testdata/golden/App.cs.json @@ -1,5 +1,5 @@ { - "version": "11", + "version": "12", "path": "App.cs", "language": "csharp", "content_hash": "8991919e54610418b58a53fb6b26dc563c5ef5af684165b60e8af5b6e7e8773c", diff --git a/plugin/indexer/testdata/golden/App.java.json b/plugin/indexer/testdata/golden/App.java.json index e18ad31c..c0de344a 100644 --- a/plugin/indexer/testdata/golden/App.java.json +++ b/plugin/indexer/testdata/golden/App.java.json @@ -1,5 +1,5 @@ { - "version": "11", + "version": "12", "path": "App.java", "language": "java", "content_hash": "4b93f5c2c4ed8e1881cb857a9b7cd65c3a0d4cb4cd0929140539d0a418a27a95", diff --git a/plugin/indexer/testdata/golden/App.kt.json b/plugin/indexer/testdata/golden/App.kt.json index a82b34c4..be6dbb5a 100644 --- a/plugin/indexer/testdata/golden/App.kt.json +++ b/plugin/indexer/testdata/golden/App.kt.json @@ -1,5 +1,5 @@ { - "version": "11", + "version": "12", "path": "App.kt", "language": "kotlin", "content_hash": "458147a6c6ebc2561187207bb3bca7e091914bfd2e52f0540f58887976983462", diff --git a/plugin/indexer/testdata/golden/app.go.json b/plugin/indexer/testdata/golden/app.go.json index 1de97921..bc978c06 100644 --- a/plugin/indexer/testdata/golden/app.go.json +++ b/plugin/indexer/testdata/golden/app.go.json @@ -1,5 +1,5 @@ { - "version": "11", + "version": "12", "path": "app.go", "language": "go", "content_hash": "3d65f36ed0edfd5e009c06d07d3d038337e9faebaca6c9762b5c473a14826166", diff --git a/plugin/indexer/testdata/golden/app.js.json b/plugin/indexer/testdata/golden/app.js.json index 8257cb5a..3f4d28dc 100644 --- a/plugin/indexer/testdata/golden/app.js.json +++ b/plugin/indexer/testdata/golden/app.js.json @@ -1,5 +1,5 @@ { - "version": "11", + "version": "12", "path": "app.js", "language": "javascript", "content_hash": "247fdd512217fb5e7ce19cbf3f60b327e145abc15a26ee510e167cbc651b0a54", diff --git a/plugin/indexer/testdata/golden/app.php.json b/plugin/indexer/testdata/golden/app.php.json index f7b828de..24b1237b 100644 --- a/plugin/indexer/testdata/golden/app.php.json +++ b/plugin/indexer/testdata/golden/app.php.json @@ -1,5 +1,5 @@ { - "version": "11", + "version": "12", "path": "app.php", "language": "php", "content_hash": "b57d8d52aafac8d7cbe2d19e5889e4e7b851ee71a48ea966cdd9cbfd14214466", diff --git a/plugin/indexer/testdata/golden/app.py.json b/plugin/indexer/testdata/golden/app.py.json index 97bb677c..1245b895 100644 --- a/plugin/indexer/testdata/golden/app.py.json +++ b/plugin/indexer/testdata/golden/app.py.json @@ -1,5 +1,5 @@ { - "version": "11", + "version": "12", "path": "app.py", "language": "python", "content_hash": "a9cdb5fb012d2a29970c07deda9cc8360983015e2360cb9091bca7bfce5c801d", diff --git a/plugin/indexer/testdata/golden/app.rb.json b/plugin/indexer/testdata/golden/app.rb.json index 2b3b13f9..d84f0b64 100644 --- a/plugin/indexer/testdata/golden/app.rb.json +++ b/plugin/indexer/testdata/golden/app.rb.json @@ -1,5 +1,5 @@ { - "version": "11", + "version": "12", "path": "app.rb", "language": "ruby", "content_hash": "f3a3e601660e0130750119df4267cac37972c412ccc8f5d8f16e3ba516f659d9", diff --git a/plugin/indexer/testdata/golden/app.rs.json b/plugin/indexer/testdata/golden/app.rs.json index 92011f3a..fcf3c271 100644 --- a/plugin/indexer/testdata/golden/app.rs.json +++ b/plugin/indexer/testdata/golden/app.rs.json @@ -1,5 +1,5 @@ { - "version": "11", + "version": "12", "path": "app.rs", "language": "rust", "content_hash": "f07a751f9610f47e7b49679223d4ca80f85ecb21abde61488935d2f21a5c3603", diff --git a/plugin/indexer/testdata/golden/app.ts.json b/plugin/indexer/testdata/golden/app.ts.json index d61ed00c..3e1d08ed 100644 --- a/plugin/indexer/testdata/golden/app.ts.json +++ b/plugin/indexer/testdata/golden/app.ts.json @@ -1,5 +1,5 @@ { - "version": "11", + "version": "12", "path": "app.ts", "language": "typescript", "content_hash": "d3908948ab8ec7b3adc898e0707ce74bb3a2d6393a724dfbeb67147840397234", diff --git a/plugin/indexer/testdata/golden/exports.js.json b/plugin/indexer/testdata/golden/exports.js.json index 0d68280d..85da7d88 100644 --- a/plugin/indexer/testdata/golden/exports.js.json +++ b/plugin/indexer/testdata/golden/exports.js.json @@ -1,5 +1,5 @@ { - "version": "11", + "version": "12", "path": "exports.js", "language": "javascript", "content_hash": "765716b5977b05d7bbc4ef5e0192e196e0209400366036053df632d54fdb0f95", diff --git a/plugin/indexer/testdata/golden/exports.py.json b/plugin/indexer/testdata/golden/exports.py.json index 3287b4ba..307cfa11 100644 --- a/plugin/indexer/testdata/golden/exports.py.json +++ b/plugin/indexer/testdata/golden/exports.py.json @@ -1,5 +1,5 @@ { - "version": "11", + "version": "12", "path": "exports.py", "language": "python", "content_hash": "653115ddc22536763049e9209d4942bca5c6817b98bc01722879b69c04d8605b", diff --git a/plugin/indexer/testdata/golden/exports.ts.json b/plugin/indexer/testdata/golden/exports.ts.json index be4306a3..f22cd269 100644 --- a/plugin/indexer/testdata/golden/exports.ts.json +++ b/plugin/indexer/testdata/golden/exports.ts.json @@ -1,5 +1,5 @@ { - "version": "11", + "version": "12", "path": "exports.ts", "language": "typescript", "content_hash": "0edcb9324080b924f3ac45d57f5b5cbe90d625038a0dd7f99fa9e5fcbeee1edc", diff --git a/plugin/indexer/testdata/golden/flags.js.json b/plugin/indexer/testdata/golden/flags.js.json index 1fa276f7..86bb9b56 100644 --- a/plugin/indexer/testdata/golden/flags.js.json +++ b/plugin/indexer/testdata/golden/flags.js.json @@ -1,5 +1,5 @@ { - "version": "11", + "version": "12", "path": "flags.js", "language": "javascript", "content_hash": "70731d0448bc5af9ecf5c81156eefbd39535a3fc674fffc250937d76c2ba4610", diff --git a/plugin/indexer/testdata/golden/flags.py.json b/plugin/indexer/testdata/golden/flags.py.json index d4a3382c..bcd5396d 100644 --- a/plugin/indexer/testdata/golden/flags.py.json +++ b/plugin/indexer/testdata/golden/flags.py.json @@ -1,5 +1,5 @@ { - "version": "11", + "version": "12", "path": "flags.py", "language": "python", "content_hash": "d3169e0c3c00508c3f9492866cf4db99ddd780d42dc9e4438558d1d4a22f2621", diff --git a/plugin/indexer/testdata/golden/flags.ts.json b/plugin/indexer/testdata/golden/flags.ts.json index a0741c03..04b60ca3 100644 --- a/plugin/indexer/testdata/golden/flags.ts.json +++ b/plugin/indexer/testdata/golden/flags.ts.json @@ -1,5 +1,5 @@ { - "version": "11", + "version": "12", "path": "flags.ts", "language": "typescript", "content_hash": "a84c4ad21735c1c250e33317463237749489ea1f220a93f97e2bea3f07b040c4", diff --git a/projectgraph/testdata/javascript/barrel-chain/indexes.json b/projectgraph/testdata/javascript/barrel-chain/indexes.json index b5828e08..2f008821 100644 --- a/projectgraph/testdata/javascript/barrel-chain/indexes.json +++ b/projectgraph/testdata/javascript/barrel-chain/indexes.json @@ -1,22 +1,22 @@ [ { - "version": "11", "path": "src/main.js", "language": "javascript", + "version": "12", "path": "src/main.js", "language": "javascript", "imports": [{"line": 1, "module": "./api", "item": "parse", "alias": "parse", "kind": "static", "namespace": ".//api//parse"}], "definitions": [{"key": "src/main.js", "kind": "module", "name": "main", "start_line": 1, "end_line": 2, "exported": true}], "calls": [{"caller": "src/main.js", "line": 2, "column": 1, "callee": "parse", "targets": [".//api//parse"], "kind": "call"}] }, { - "version": "11", "path": "src/api/index.js", "language": "javascript", + "version": "12", "path": "src/api/index.js", "language": "javascript", "exports": [{"name": "parse", "target": "", "from": "../core", "item": "parse", "line": 1}], "definitions": [{"key": "src/api/index.js", "kind": "module", "name": "index", "start_line": 1, "end_line": 1, "exported": true}] }, { - "version": "11", "path": "src/core/index.js", "language": "javascript", + "version": "12", "path": "src/core/index.js", "language": "javascript", "exports": [{"name": "parse", "target": "", "from": "./parse", "item": "doParse", "line": 1}], "definitions": [{"key": "src/core/index.js", "kind": "module", "name": "index", "start_line": 1, "end_line": 1, "exported": true}] }, { - "version": "11", "path": "src/core/parse.js", "language": "javascript", + "version": "12", "path": "src/core/parse.js", "language": "javascript", "exports": [{"name": "doParse", "target": "src/core/parse.js//doParse", "line": 1}], "definitions": [ {"key": "src/core/parse.js", "kind": "module", "name": "parse", "start_line": 1, "end_line": 3, "exported": true}, diff --git a/projectgraph/testdata/javascript/commonjs-require/indexes.json b/projectgraph/testdata/javascript/commonjs-require/indexes.json index b4b7e24f..9ca52585 100644 --- a/projectgraph/testdata/javascript/commonjs-require/indexes.json +++ b/projectgraph/testdata/javascript/commonjs-require/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "11", "path": "main.js", "language": "javascript", + "version": "12", "path": "main.js", "language": "javascript", "imports": [ {"line": 1, "module": "./lib", "alias": "utils", "kind": "static", "namespace": ".//lib"}, {"line": 2, "module": "./helper", "item": "helper", "alias": "helper", "kind": "static", "namespace": ".//helper//helper"} @@ -12,7 +12,7 @@ ] }, { - "version": "11", "path": "lib/index.js", "language": "javascript", + "version": "12", "path": "lib/index.js", "language": "javascript", "exports": [{"name": "run", "target": "lib/index.js//run", "line": 3}], "definitions": [ {"key": "lib/index.js", "kind": "module", "name": "index", "start_line": 1, "end_line": 3, "exported": true}, @@ -20,7 +20,7 @@ ] }, { - "version": "11", "path": "helper.js", "language": "javascript", + "version": "12", "path": "helper.js", "language": "javascript", "exports": [{"name": "helper", "target": "helper.js//helper", "line": 3}], "definitions": [ {"key": "helper.js", "kind": "module", "name": "helper", "start_line": 1, "end_line": 3, "exported": true}, diff --git a/projectgraph/testdata/javascript/default-import/indexes.json b/projectgraph/testdata/javascript/default-import/indexes.json index 8cc3dc0f..8e5bbad0 100644 --- a/projectgraph/testdata/javascript/default-import/indexes.json +++ b/projectgraph/testdata/javascript/default-import/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "11", "path": "main.js", "language": "javascript", + "version": "12", "path": "main.js", "language": "javascript", "imports": [ {"line": 1, "module": "./log", "alias": "log", "kind": "static", "namespace": ".//log"}, {"line": 2, "module": "./make", "alias": "make", "kind": "static", "namespace": ".//make"} @@ -12,7 +12,7 @@ ] }, { - "version": "11", "path": "log.js", "language": "javascript", + "version": "12", "path": "log.js", "language": "javascript", "exports": [{"name": "default", "target": "log.js//log", "line": 1}], "definitions": [ {"key": "log.js", "kind": "module", "name": "log", "start_line": 1, "end_line": 1, "exported": true}, @@ -20,7 +20,7 @@ ] }, { - "version": "11", "path": "make.js", "language": "javascript", + "version": "12", "path": "make.js", "language": "javascript", "exports": [{"name": "default", "target": "make.js//make", "line": 1}], "definitions": [ {"key": "make.js", "kind": "module", "name": "make", "start_line": 1, "end_line": 1, "exported": true}, diff --git a/projectgraph/testdata/javascript/esm-named-import/indexes.json b/projectgraph/testdata/javascript/esm-named-import/indexes.json index c1a44fbc..88700bac 100644 --- a/projectgraph/testdata/javascript/esm-named-import/indexes.json +++ b/projectgraph/testdata/javascript/esm-named-import/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "11", "path": "src/main.js", "language": "javascript", + "version": "12", "path": "src/main.js", "language": "javascript", "imports": [ {"line": 1, "module": "./parser.js", "item": "parse", "alias": "parse", "kind": "static", "namespace": ".//parser.js//parse"}, {"line": 2, "module": "./util", "item": "fmt", "alias": "fmt", "kind": "static", "namespace": ".//util//fmt"}, @@ -14,7 +14,7 @@ "unresolved": [{"caller": "src/main.js", "line": 5, "column": 1, "text": "import", "reason": "dynamic-import"}] }, { - "version": "11", "path": "src/parser.js", "language": "javascript", + "version": "12", "path": "src/parser.js", "language": "javascript", "imports": [{"line": 1, "module": "js-yaml", "alias": "yaml", "kind": "static", "namespace": "js-yaml"}], "exports": [{"name": "parse", "target": "src/parser.js//parse", "line": 2}], "definitions": [ @@ -24,7 +24,7 @@ "calls": [{"caller": "src/parser.js//parse", "line": 3, "column": 10, "callee": "yaml.load", "targets": ["js-yaml//load"], "receiver": "yaml", "method": "load", "kind": "call"}] }, { - "version": "11", "path": "src/util.js", "language": "javascript", + "version": "12", "path": "src/util.js", "language": "javascript", "exports": [{"name": "fmt", "target": "src/util.js//fmt", "line": 1}], "definitions": [ {"key": "src/util.js", "kind": "module", "name": "util", "start_line": 1, "end_line": 1, "exported": true}, diff --git a/projectgraph/testdata/javascript/export-star/indexes.json b/projectgraph/testdata/javascript/export-star/indexes.json index 6f5a0b20..58526a72 100644 --- a/projectgraph/testdata/javascript/export-star/indexes.json +++ b/projectgraph/testdata/javascript/export-star/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "11", "path": "main.js", "language": "javascript", + "version": "12", "path": "main.js", "language": "javascript", "imports": [ {"line": 1, "module": "./all", "item": "a", "alias": "a", "kind": "static", "namespace": ".//all//a"}, {"line": 1, "module": "./all", "item": "b", "alias": "b", "kind": "static", "namespace": ".//all//b"}, @@ -14,7 +14,7 @@ ] }, { - "version": "11", "path": "all.js", "language": "javascript", + "version": "12", "path": "all.js", "language": "javascript", "exports": [ {"name": "*", "target": "", "from": "./a", "item": "*", "line": 1}, {"name": "*", "target": "", "from": "./b", "item": "*", "line": 2}, @@ -23,7 +23,7 @@ "definitions": [{"key": "all.js", "kind": "module", "name": "all", "start_line": 1, "end_line": 3, "exported": true}] }, { - "version": "11", "path": "a.js", "language": "javascript", + "version": "12", "path": "a.js", "language": "javascript", "exports": [{"name": "a", "target": "a.js//a", "line": 1}], "definitions": [ {"key": "a.js", "kind": "module", "name": "a", "start_line": 1, "end_line": 1, "exported": true}, @@ -31,7 +31,7 @@ ] }, { - "version": "11", "path": "b.js", "language": "javascript", + "version": "12", "path": "b.js", "language": "javascript", "exports": [{"name": "b", "target": "b.js//b", "line": 1}], "definitions": [ {"key": "b.js", "kind": "module", "name": "b", "start_line": 1, "end_line": 1, "exported": true}, @@ -39,7 +39,7 @@ ] }, { - "version": "11", "path": "c.js", "language": "javascript", + "version": "12", "path": "c.js", "language": "javascript", "exports": [{"name": "c", "target": "c.js//c", "line": 1}], "definitions": [ {"key": "c.js", "kind": "module", "name": "c", "start_line": 1, "end_line": 1, "exported": true}, diff --git a/projectgraph/testdata/javascript/exports-conditions/indexes.json b/projectgraph/testdata/javascript/exports-conditions/indexes.json index a74f2959..823785e0 100644 --- a/projectgraph/testdata/javascript/exports-conditions/indexes.json +++ b/projectgraph/testdata/javascript/exports-conditions/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "11", "path": "app/main.mjs", "language": "javascript", + "version": "12", "path": "app/main.mjs", "language": "javascript", "imports": [ {"line": 1, "module": "@acme/lib", "item": "x", "alias": "x", "kind": "static", "namespace": "@acme//lib//x"}, {"line": 2, "module": "@acme/lib/feature/y", "item": "y", "alias": "y", "kind": "static", "namespace": "@acme//lib//feature//y//y"} @@ -12,7 +12,7 @@ ] }, { - "version": "11", "path": "lib/esm/index.mjs", "language": "javascript", + "version": "12", "path": "lib/esm/index.mjs", "language": "javascript", "exports": [{"name": "x", "target": "lib/esm/index.mjs//x", "line": 1}], "definitions": [ {"key": "lib/esm/index.mjs", "kind": "module", "name": "index", "start_line": 1, "end_line": 1, "exported": true}, @@ -20,7 +20,7 @@ ] }, { - "version": "11", "path": "lib/cjs/index.cjs", "language": "javascript", + "version": "12", "path": "lib/cjs/index.cjs", "language": "javascript", "exports": [{"name": "x", "target": "lib/cjs/index.cjs//x", "line": 1}], "definitions": [ {"key": "lib/cjs/index.cjs", "kind": "module", "name": "index", "start_line": 1, "end_line": 1, "exported": true}, @@ -28,7 +28,7 @@ ] }, { - "version": "11", "path": "lib/src/feature/y.js", "language": "javascript", + "version": "12", "path": "lib/src/feature/y.js", "language": "javascript", "exports": [{"name": "y", "target": "lib/src/feature/y.js//y", "line": 1}], "definitions": [ {"key": "lib/src/feature/y.js", "kind": "module", "name": "y", "start_line": 1, "end_line": 1, "exported": true}, diff --git a/projectgraph/testdata/javascript/unknown-receiver/indexes.json b/projectgraph/testdata/javascript/unknown-receiver/indexes.json index 2015bb45..bba27ba3 100644 --- a/projectgraph/testdata/javascript/unknown-receiver/indexes.json +++ b/projectgraph/testdata/javascript/unknown-receiver/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "11", + "version": "12", "path": "main.js", "language": "javascript", "imports": [ diff --git a/projectgraph/testdata/javascript/workspaces/indexes.json b/projectgraph/testdata/javascript/workspaces/indexes.json index 3796b0a0..a6137edb 100644 --- a/projectgraph/testdata/javascript/workspaces/indexes.json +++ b/projectgraph/testdata/javascript/workspaces/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "11", "path": "packages/app/src/main.js", "language": "javascript", + "version": "12", "path": "packages/app/src/main.js", "language": "javascript", "imports": [ {"line": 1, "module": "@acme/core", "item": "start", "alias": "start", "kind": "static", "namespace": "@acme//core//start"}, {"line": 2, "module": "@acme/core/util", "item": "tidy", "alias": "tidy", "kind": "static", "namespace": "@acme//core//util//tidy"}, @@ -14,7 +14,7 @@ ] }, { - "version": "11", "path": "packages/core/lib/index.js", "language": "javascript", + "version": "12", "path": "packages/core/lib/index.js", "language": "javascript", "exports": [{"name": "start", "target": "packages/core/lib/index.js//start", "line": 1}], "definitions": [ {"key": "packages/core/lib/index.js", "kind": "module", "name": "index", "start_line": 1, "end_line": 1, "exported": true}, @@ -22,7 +22,7 @@ ] }, { - "version": "11", "path": "packages/core/util.js", "language": "javascript", + "version": "12", "path": "packages/core/util.js", "language": "javascript", "exports": [{"name": "tidy", "target": "packages/core/util.js//tidy", "line": 1}], "definitions": [ {"key": "packages/core/util.js", "kind": "module", "name": "util", "start_line": 1, "end_line": 1, "exported": true}, diff --git a/projectgraph/testdata/python/all-hides/indexes.json b/projectgraph/testdata/python/all-hides/indexes.json index d16db4c3..62a1d789 100644 --- a/projectgraph/testdata/python/all-hides/indexes.json +++ b/projectgraph/testdata/python/all-hides/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "11", "path": "main.py", "language": "python", + "version": "12", "path": "main.py", "language": "python", "imports": [ {"line": 1, "module": "pkg", "item": "f", "alias": "f", "kind": "static", "namespace": "pkg//f"}, {"line": 1, "module": "pkg", "item": "g", "alias": "g", "kind": "static", "namespace": "pkg//g"} @@ -12,7 +12,7 @@ ] }, { - "version": "11", "path": "pkg/__init__.py", "language": "python", + "version": "12", "path": "pkg/__init__.py", "language": "python", "imports": [ {"line": 1, "module": ".a", "item": "f", "alias": "f", "kind": "static", "namespace": "//a//f"}, {"line": 1, "module": ".a", "item": "g", "alias": "g", "kind": "static", "namespace": "//a//g"} @@ -21,7 +21,7 @@ "definitions": [{"key": "pkg/__init__.py", "kind": "module", "name": "__init__", "start_line": 1, "end_line": 2, "exported": true}] }, { - "version": "11", "path": "pkg/a.py", "language": "python", + "version": "12", "path": "pkg/a.py", "language": "python", "definitions": [ {"key": "pkg/a.py", "kind": "module", "name": "a", "start_line": 1, "end_line": 4, "exported": true}, {"key": "pkg/a.py//f", "kind": "function", "name": "f", "start_line": 1, "end_line": 2, "exported": true}, diff --git a/projectgraph/testdata/python/assignment/indexes.json b/projectgraph/testdata/python/assignment/indexes.json index fb85ac4c..c89aa26b 100644 --- a/projectgraph/testdata/python/assignment/indexes.json +++ b/projectgraph/testdata/python/assignment/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "11", "path": "main.py", "language": "python", + "version": "12", "path": "main.py", "language": "python", "imports": [{"line": 1, "module": "yaml", "alias": "yaml", "kind": "static", "namespace": "yaml"}], "definitions": [ {"key": "main.py", "kind": "module", "name": "main", "start_line": 1, "end_line": 7, "exported": true}, diff --git a/projectgraph/testdata/python/class-body/indexes.json b/projectgraph/testdata/python/class-body/indexes.json index 03b1db43..6c7fc685 100644 --- a/projectgraph/testdata/python/class-body/indexes.json +++ b/projectgraph/testdata/python/class-body/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "11", + "version": "12", "path": "config.py", "language": "python", "imports": [ diff --git a/projectgraph/testdata/python/decorators/indexes.json b/projectgraph/testdata/python/decorators/indexes.json index 6733d0da..959f8ce3 100644 --- a/projectgraph/testdata/python/decorators/indexes.json +++ b/projectgraph/testdata/python/decorators/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "11", + "version": "12", "path": "app.py", "language": "python", "imports": [ diff --git a/projectgraph/testdata/python/external/indexes.json b/projectgraph/testdata/python/external/indexes.json index a096c376..ae85c90b 100644 --- a/projectgraph/testdata/python/external/indexes.json +++ b/projectgraph/testdata/python/external/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "11", "path": "main.py", "language": "python", + "version": "12", "path": "main.py", "language": "python", "imports": [ {"line": 1, "module": "requests", "alias": "requests", "kind": "static", "namespace": "requests"}, {"line": 2, "module": "yaml", "item": "load", "alias": "yl", "kind": "static", "namespace": "yaml//load"}, diff --git a/projectgraph/testdata/python/local-receiver/indexes.json b/projectgraph/testdata/python/local-receiver/indexes.json index b93898f0..29c0ed1d 100644 --- a/projectgraph/testdata/python/local-receiver/indexes.json +++ b/projectgraph/testdata/python/local-receiver/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "11", + "version": "12", "path": "main.py", "language": "python", "imports": [ diff --git a/projectgraph/testdata/python/member-call/indexes.json b/projectgraph/testdata/python/member-call/indexes.json index e7eed657..e451c9c9 100644 --- a/projectgraph/testdata/python/member-call/indexes.json +++ b/projectgraph/testdata/python/member-call/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "11", "path": "main.py", "language": "python", + "version": "12", "path": "main.py", "language": "python", "imports": [{"line": 1, "module": "parser", "item": "Parser", "alias": "Parser", "kind": "static", "namespace": "parser//Parser"}], "definitions": [{"key": "main.py", "kind": "module", "name": "main", "start_line": 1, "end_line": 4, "exported": true}], "calls": [ @@ -10,7 +10,7 @@ ] }, { - "version": "11", "path": "parser.py", "language": "python", + "version": "12", "path": "parser.py", "language": "python", "imports": [{"line": 1, "module": "base", "item": "Base", "alias": "Base", "kind": "static", "namespace": "base//Base"}], "definitions": [ {"key": "parser.py", "kind": "module", "name": "parser", "start_line": 1, "end_line": 7, "exported": true}, @@ -22,7 +22,7 @@ "calls": [{"caller": "parser.py//Parser//parse", "line": 6, "column": 9, "callee": "self.helper", "targets": ["parser.py//Parser//helper"], "receiver": "self", "method": "helper", "kind": "call"}] }, { - "version": "11", "path": "base.py", "language": "python", + "version": "12", "path": "base.py", "language": "python", "imports": [{"line": 1, "module": "io", "alias": "io", "kind": "static", "namespace": "io"}], "definitions": [ {"key": "base.py", "kind": "module", "name": "base", "start_line": 1, "end_line": 4, "exported": true}, diff --git a/projectgraph/testdata/python/missing-file/indexes.json b/projectgraph/testdata/python/missing-file/indexes.json index 1f481508..59c334be 100644 --- a/projectgraph/testdata/python/missing-file/indexes.json +++ b/projectgraph/testdata/python/missing-file/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "11", "path": "main.py", "language": "python", + "version": "12", "path": "main.py", "language": "python", "imports": [{"line": 1, "module": "handlers", "item": "parse", "alias": "parse", "kind": "static", "namespace": "handlers//parse"}], "definitions": [{"key": "main.py", "kind": "module", "name": "main", "start_line": 1, "end_line": 2, "exported": true}], "calls": [{"caller": "main.py", "line": 2, "column": 1, "callee": "parse", "targets": ["handlers//parse"], "kind": "call"}] diff --git a/projectgraph/testdata/python/package-init/indexes.json b/projectgraph/testdata/python/package-init/indexes.json index e57f197c..2f2aa9a3 100644 --- a/projectgraph/testdata/python/package-init/indexes.json +++ b/projectgraph/testdata/python/package-init/indexes.json @@ -1,17 +1,17 @@ [ { - "version": "11", "path": "main.py", "language": "python", + "version": "12", "path": "main.py", "language": "python", "imports": [{"line": 1, "module": "pkg", "item": "load", "alias": "load", "kind": "static", "namespace": "pkg//load"}], "definitions": [{"key": "main.py", "kind": "module", "name": "main", "start_line": 1, "end_line": 2, "exported": true}], "calls": [{"caller": "main.py", "line": 2, "column": 1, "callee": "load", "targets": ["pkg//load"], "kind": "call"}] }, { - "version": "11", "path": "pkg/__init__.py", "language": "python", + "version": "12", "path": "pkg/__init__.py", "language": "python", "imports": [{"line": 1, "module": ".loader", "item": "load", "alias": "load", "kind": "static", "namespace": "//loader//load"}], "definitions": [{"key": "pkg/__init__.py", "kind": "module", "name": "__init__", "start_line": 1, "end_line": 1, "exported": true}] }, { - "version": "11", "path": "pkg/loader.py", "language": "python", + "version": "12", "path": "pkg/loader.py", "language": "python", "imports": [{"line": 1, "module": "yaml", "alias": "yaml", "kind": "static", "namespace": "yaml"}], "definitions": [ {"key": "pkg/loader.py", "kind": "module", "name": "loader", "start_line": 1, "end_line": 4, "exported": true}, diff --git a/projectgraph/testdata/python/reexport-cycle/indexes.json b/projectgraph/testdata/python/reexport-cycle/indexes.json index c73126f5..ed02d876 100644 --- a/projectgraph/testdata/python/reexport-cycle/indexes.json +++ b/projectgraph/testdata/python/reexport-cycle/indexes.json @@ -1,17 +1,17 @@ [ { - "version": "11", "path": "main.py", "language": "python", + "version": "12", "path": "main.py", "language": "python", "imports": [{"line": 1, "module": "a", "item": "x", "alias": "x", "kind": "static", "namespace": "a//x"}], "definitions": [{"key": "main.py", "kind": "module", "name": "main", "start_line": 1, "end_line": 2, "exported": true}], "calls": [{"caller": "main.py", "line": 2, "column": 1, "callee": "x", "targets": ["a//x"], "kind": "call"}] }, { - "version": "11", "path": "a/__init__.py", "language": "python", + "version": "12", "path": "a/__init__.py", "language": "python", "imports": [{"line": 1, "module": "b", "item": "x", "alias": "x", "kind": "static", "namespace": "b//x"}], "definitions": [{"key": "a/__init__.py", "kind": "module", "name": "__init__", "start_line": 1, "end_line": 1, "exported": true}] }, { - "version": "11", "path": "b/__init__.py", "language": "python", + "version": "12", "path": "b/__init__.py", "language": "python", "imports": [{"line": 1, "module": "a", "item": "x", "alias": "x", "kind": "static", "namespace": "a//x"}], "definitions": [{"key": "b/__init__.py", "kind": "module", "name": "__init__", "start_line": 1, "end_line": 1, "exported": true}] } diff --git a/projectgraph/testdata/python/relative-imports/indexes.json b/projectgraph/testdata/python/relative-imports/indexes.json index 9ee7e1e0..c33fb94a 100644 --- a/projectgraph/testdata/python/relative-imports/indexes.json +++ b/projectgraph/testdata/python/relative-imports/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "11", "path": "pkg/sub/a.py", "language": "python", + "version": "12", "path": "pkg/sub/a.py", "language": "python", "imports": [ {"line": 1, "module": ".b", "item": "f", "alias": "f", "kind": "static", "namespace": "//b//f"}, {"line": 2, "module": "..c", "item": "g", "alias": "g", "kind": "static", "namespace": "////c//g"} @@ -15,14 +15,14 @@ ] }, { - "version": "11", "path": "pkg/sub/b.py", "language": "python", + "version": "12", "path": "pkg/sub/b.py", "language": "python", "definitions": [ {"key": "pkg/sub/b.py", "kind": "module", "name": "b", "start_line": 1, "end_line": 2, "exported": true}, {"key": "pkg/sub/b.py//f", "kind": "function", "name": "f", "start_line": 1, "end_line": 2, "exported": true} ] }, { - "version": "11", "path": "pkg/c.py", "language": "python", + "version": "12", "path": "pkg/c.py", "language": "python", "definitions": [ {"key": "pkg/c.py", "kind": "module", "name": "c", "start_line": 1, "end_line": 2, "exported": true}, {"key": "pkg/c.py//g", "kind": "function", "name": "g", "start_line": 1, "end_line": 2, "exported": true} diff --git a/projectgraph/testdata/python/sibling-import/indexes.json b/projectgraph/testdata/python/sibling-import/indexes.json index 7aa4de6f..8b3e383b 100644 --- a/projectgraph/testdata/python/sibling-import/indexes.json +++ b/projectgraph/testdata/python/sibling-import/indexes.json @@ -1,12 +1,12 @@ [ { - "version": "11", "path": "main.py", "language": "python", + "version": "12", "path": "main.py", "language": "python", "imports": [{"line": 1, "module": "handlers", "item": "parse", "alias": "parse", "kind": "static", "namespace": "handlers//parse"}], "definitions": [{"key": "main.py", "kind": "module", "name": "main", "start_line": 1, "end_line": 3, "exported": true}], "calls": [{"caller": "main.py", "line": 3, "column": 1, "callee": "parse", "targets": ["handlers//parse"], "kind": "call"}] }, { - "version": "11", "path": "handlers.py", "language": "python", + "version": "12", "path": "handlers.py", "language": "python", "imports": [{"line": 1, "module": "yaml", "alias": "yaml", "kind": "static", "namespace": "yaml"}], "definitions": [ {"key": "handlers.py", "kind": "module", "name": "handlers", "start_line": 1, "end_line": 4, "exported": true}, diff --git a/projectgraph/testdata/python/src-layout/indexes.json b/projectgraph/testdata/python/src-layout/indexes.json index b659b88c..1092ace6 100644 --- a/projectgraph/testdata/python/src-layout/indexes.json +++ b/projectgraph/testdata/python/src-layout/indexes.json @@ -1,16 +1,16 @@ [ { - "version": "11", "path": "services/api/src/api/__init__.py", "language": "python", + "version": "12", "path": "services/api/src/api/__init__.py", "language": "python", "definitions": [{"key": "services/api/src/api/__init__.py", "kind": "module", "name": "__init__", "start_line": 1, "end_line": 1, "exported": true}] }, { - "version": "11", "path": "services/api/src/api/cli.py", "language": "python", + "version": "12", "path": "services/api/src/api/cli.py", "language": "python", "imports": [{"line": 1, "module": "api.core", "item": "run", "alias": "run", "kind": "static", "namespace": "api//core//run"}], "definitions": [{"key": "services/api/src/api/cli.py", "kind": "module", "name": "cli", "start_line": 1, "end_line": 2, "exported": true}], "calls": [{"caller": "services/api/src/api/cli.py", "line": 2, "column": 1, "callee": "run", "targets": ["api//core//run"], "kind": "call"}] }, { - "version": "11", "path": "services/api/src/api/core.py", "language": "python", + "version": "12", "path": "services/api/src/api/core.py", "language": "python", "definitions": [ {"key": "services/api/src/api/core.py", "kind": "module", "name": "core", "start_line": 1, "end_line": 2, "exported": true}, {"key": "services/api/src/api/core.py//run", "kind": "function", "name": "run", "start_line": 1, "end_line": 2, "exported": true} diff --git a/projectgraph/testdata/python/sys-path/indexes.json b/projectgraph/testdata/python/sys-path/indexes.json index fc4c5952..3833cba8 100644 --- a/projectgraph/testdata/python/sys-path/indexes.json +++ b/projectgraph/testdata/python/sys-path/indexes.json @@ -1,12 +1,12 @@ [ { - "version": "11", "path": "app/main.py", "language": "python", + "version": "12", "path": "app/main.py", "language": "python", "imports": [{"line": 1, "module": "shared", "alias": "shared", "kind": "static", "namespace": "shared"}], "definitions": [{"key": "app/main.py", "kind": "module", "name": "main", "start_line": 1, "end_line": 2, "exported": true}], "calls": [{"caller": "app/main.py", "line": 2, "column": 1, "callee": "shared.go", "targets": ["shared//go"], "receiver": "shared", "method": "go", "kind": "call"}] }, { - "version": "11", "path": "lib/shared.py", "language": "python", + "version": "12", "path": "lib/shared.py", "language": "python", "definitions": [ {"key": "lib/shared.py", "kind": "module", "name": "shared", "start_line": 1, "end_line": 2, "exported": true}, {"key": "lib/shared.py//go", "kind": "function", "name": "go", "start_line": 1, "end_line": 2, "exported": true} diff --git a/projectgraph/testdata/python/wildcard/indexes.json b/projectgraph/testdata/python/wildcard/indexes.json index cab119cf..46228204 100644 --- a/projectgraph/testdata/python/wildcard/indexes.json +++ b/projectgraph/testdata/python/wildcard/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "11", "path": "main.py", "language": "python", + "version": "12", "path": "main.py", "language": "python", "imports": [{"line": 1, "module": "util", "wildcard": true, "kind": "static", "namespace": "util"}], "definitions": [{"key": "main.py", "kind": "module", "name": "main", "start_line": 1, "end_line": 3, "exported": true}], "calls": [ @@ -9,7 +9,7 @@ ] }, { - "version": "11", "path": "util.py", "language": "python", + "version": "12", "path": "util.py", "language": "python", "definitions": [ {"key": "util.py", "kind": "module", "name": "util", "start_line": 1, "end_line": 4, "exported": true}, {"key": "util.py//helper", "kind": "function", "name": "helper", "start_line": 1, "end_line": 2, "exported": true}, diff --git a/projectgraph/testdata/roots/extra-rules/indexes.json b/projectgraph/testdata/roots/extra-rules/indexes.json index b69e638a..76047465 100644 --- a/projectgraph/testdata/roots/extra-rules/indexes.json +++ b/projectgraph/testdata/roots/extra-rules/indexes.json @@ -1,10 +1,10 @@ [ { - "version": "11", "path": "app/users/route.ts", "language": "typescript", + "version": "12", "path": "app/users/route.ts", "language": "typescript", "definitions": [{"key": "app/users/route.ts", "kind": "module", "name": "route", "start_line": 1, "end_line": 1, "exported": true}] }, { - "version": "11", "path": "src/handler.js", "language": "javascript", + "version": "12", "path": "src/handler.js", "language": "javascript", "definitions": [ {"key": "src/handler.js", "kind": "module", "name": "handler", "start_line": 1, "end_line": 4, "exported": true}, {"key": "src/handler.js//Handler", "kind": "class", "name": "Handler", "start_line": 1, "end_line": 4, "exported": true}, @@ -13,7 +13,7 @@ ] }, { - "version": "11", "path": "views.py", "language": "python", + "version": "12", "path": "views.py", "language": "python", "definitions": [ {"key": "views.py", "kind": "module", "name": "views", "start_line": 1, "end_line": 3, "exported": true}, {"key": "views.py//index", "kind": "function", "name": "index", "start_line": 2, "end_line": 3, "exported": true, "decorators": ["@app.route"]} diff --git a/projectgraph/testdata/roots/library-surface/indexes.json b/projectgraph/testdata/roots/library-surface/indexes.json index ee7a7d8e..670c5abd 100644 --- a/projectgraph/testdata/roots/library-surface/indexes.json +++ b/projectgraph/testdata/roots/library-surface/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "11", "path": "lib/api.py", "language": "python", + "version": "12", "path": "lib/api.py", "language": "python", "definitions": [ {"key": "lib/api.py", "kind": "module", "name": "api", "start_line": 1, "end_line": 4, "exported": true}, {"key": "lib/api.py//public", "kind": "function", "name": "public", "start_line": 1, "end_line": 2, "exported": true}, @@ -8,7 +8,7 @@ ] }, { - "version": "11", "path": "lib/test_api.py", "language": "python", + "version": "12", "path": "lib/test_api.py", "language": "python", "definitions": [ {"key": "lib/test_api.py", "kind": "module", "name": "test_api", "start_line": 1, "end_line": 2, "exported": true}, {"key": "lib/test_api.py//test_public", "kind": "function", "name": "test_public", "start_line": 1, "end_line": 2, "exported": true} diff --git a/projectgraph/testdata/roots/main-guard/indexes.json b/projectgraph/testdata/roots/main-guard/indexes.json index fdf6b04c..71973966 100644 --- a/projectgraph/testdata/roots/main-guard/indexes.json +++ b/projectgraph/testdata/roots/main-guard/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "11", "path": "app.py", "language": "python", "main_guard": true, + "version": "12", "path": "app.py", "language": "python", "main_guard": true, "definitions": [ {"key": "app.py", "kind": "module", "name": "app", "start_line": 1, "end_line": 5, "exported": true}, {"key": "app.py//run", "kind": "function", "name": "run", "start_line": 1, "end_line": 2, "exported": true} @@ -8,7 +8,7 @@ "calls": [{"caller": "app.py", "line": 5, "column": 5, "callee": "run", "targets": ["app.py//run"], "kind": "call"}] }, { - "version": "11", "path": "lib.py", "language": "python", + "version": "12", "path": "lib.py", "language": "python", "definitions": [ {"key": "lib.py", "kind": "module", "name": "lib", "start_line": 1, "end_line": 2, "exported": true}, {"key": "lib.py//lib", "kind": "function", "name": "lib", "start_line": 1, "end_line": 2, "exported": true} diff --git a/projectgraph/testdata/roots/package-json-bin/indexes.json b/projectgraph/testdata/roots/package-json-bin/indexes.json index 47af7d1c..744ee0b9 100644 --- a/projectgraph/testdata/roots/package-json-bin/indexes.json +++ b/projectgraph/testdata/roots/package-json-bin/indexes.json @@ -1,12 +1,12 @@ [ { - "version": "11", "path": "bin/cli.js", "language": "javascript", + "version": "12", "path": "bin/cli.js", "language": "javascript", "imports": [{"line": 1, "module": "../lib/run", "item": "run", "alias": "run", "kind": "static", "namespace": "..//lib//run//run"}], "definitions": [{"key": "bin/cli.js", "kind": "module", "name": "cli", "start_line": 1, "end_line": 2, "exported": true}], "calls": [{"caller": "bin/cli.js", "line": 2, "column": 1, "callee": "run", "targets": ["..//lib//run//run"], "kind": "call"}] }, { - "version": "11", "path": "lib/run.js", "language": "javascript", + "version": "12", "path": "lib/run.js", "language": "javascript", "exports": [{"name": "run", "target": "lib/run.js//run", "line": 1}], "definitions": [ {"key": "lib/run.js", "kind": "module", "name": "run", "start_line": 1, "end_line": 1, "exported": true}, diff --git a/projectgraph/testdata/roots/package-json-main/indexes.json b/projectgraph/testdata/roots/package-json-main/indexes.json index 06915977..4077565c 100644 --- a/projectgraph/testdata/roots/package-json-main/indexes.json +++ b/projectgraph/testdata/roots/package-json-main/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "11", + "version": "12", "path": "cli.js", "language": "javascript", "exports": [ @@ -30,7 +30,7 @@ ] }, { - "version": "11", + "version": "12", "path": "fmt.js", "language": "javascript", "exports": [ @@ -60,7 +60,7 @@ ] }, { - "version": "11", + "version": "12", "path": "index.js", "language": "javascript", "exports": [ @@ -104,7 +104,7 @@ ] }, { - "version": "11", + "version": "12", "path": "parser.js", "language": "javascript", "exports": [ diff --git a/projectgraph/testdata/roots/procfile-dockerfile/indexes.json b/projectgraph/testdata/roots/procfile-dockerfile/indexes.json index 6fd684cd..96452321 100644 --- a/projectgraph/testdata/roots/procfile-dockerfile/indexes.json +++ b/projectgraph/testdata/roots/procfile-dockerfile/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "11", + "version": "12", "path": "app/wsgi.py", "language": "python", "definitions": [ @@ -23,7 +23,7 @@ ] }, { - "version": "11", + "version": "12", "path": "app/cli.py", "language": "python", "definitions": [ @@ -38,7 +38,7 @@ ] }, { - "version": "11", + "version": "12", "path": "app/__init__.py", "language": "python", "definitions": [ @@ -53,7 +53,7 @@ ] }, { - "version": "11", + "version": "12", "path": "worker.py", "language": "python", "definitions": [ @@ -68,7 +68,7 @@ ] }, { - "version": "11", + "version": "12", "path": "ignored.py", "language": "python", "definitions": [ @@ -83,7 +83,7 @@ ] }, { - "version": "11", + "version": "12", "path": "web/server.js", "language": "javascript", "definitions": [ diff --git a/projectgraph/testdata/roots/pyproject-scripts/indexes.json b/projectgraph/testdata/roots/pyproject-scripts/indexes.json index 39247fca..35c50143 100644 --- a/projectgraph/testdata/roots/pyproject-scripts/indexes.json +++ b/projectgraph/testdata/roots/pyproject-scripts/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "11", + "version": "12", "path": "src/app/cli.py", "language": "python", "definitions": [ @@ -23,7 +23,7 @@ ] }, { - "version": "11", + "version": "12", "path": "src/app/tool.py", "language": "python", "definitions": [ @@ -46,7 +46,7 @@ ] }, { - "version": "11", + "version": "12", "path": "src/app/plugin.py", "language": "python", "definitions": [ @@ -78,7 +78,7 @@ ] }, { - "version": "11", + "version": "12", "path": "src/app/legacy.py", "language": "python", "definitions": [ @@ -93,7 +93,7 @@ ] }, { - "version": "11", + "version": "12", "path": "src/app/ignored.py", "language": "python", "definitions": [ diff --git a/projectgraph/testdata/roots/setup-py/indexes.json b/projectgraph/testdata/roots/setup-py/indexes.json index aa06c36a..9caf273e 100644 --- a/projectgraph/testdata/roots/setup-py/indexes.json +++ b/projectgraph/testdata/roots/setup-py/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "11", + "version": "12", "path": "tool/main.py", "language": "python", "definitions": [ @@ -23,7 +23,7 @@ ] }, { - "version": "11", + "version": "12", "path": "tool/admin.py", "language": "python", "definitions": [ diff --git a/projectgraph/testdata/roots/test-file/indexes.json b/projectgraph/testdata/roots/test-file/indexes.json index f2471837..315b5d8e 100644 --- a/projectgraph/testdata/roots/test-file/indexes.json +++ b/projectgraph/testdata/roots/test-file/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "11", "path": "tests/test_app.py", "language": "python", + "version": "12", "path": "tests/test_app.py", "language": "python", "definitions": [ {"key": "tests/test_app.py", "kind": "module", "name": "test_app", "start_line": 1, "end_line": 6, "exported": true}, {"key": "tests/test_app.py//TestApp", "kind": "class", "name": "TestApp", "start_line": 1, "end_line": 4, "exported": true}, diff --git a/projectgraph/testdata/typescript/js-specifier/indexes.json b/projectgraph/testdata/typescript/js-specifier/indexes.json index 8ce8657b..8d4c4613 100644 --- a/projectgraph/testdata/typescript/js-specifier/indexes.json +++ b/projectgraph/testdata/typescript/js-specifier/indexes.json @@ -1,12 +1,12 @@ [ { - "version": "11", "path": "src/main.ts", "language": "typescript", + "version": "12", "path": "src/main.ts", "language": "typescript", "imports": [{"line": 1, "module": "./util.js", "item": "f", "alias": "f", "kind": "static", "namespace": ".//util.js//f"}], "definitions": [{"key": "src/main.ts", "kind": "module", "name": "main", "start_line": 1, "end_line": 2, "exported": true}], "calls": [{"caller": "src/main.ts", "line": 2, "column": 1, "callee": "f", "targets": [".//util.js//f"], "kind": "call"}] }, { - "version": "11", "path": "src/util.ts", "language": "typescript", + "version": "12", "path": "src/util.ts", "language": "typescript", "exports": [{"name": "f", "target": "src/util.ts//f", "line": 1}], "definitions": [ {"key": "src/util.ts", "kind": "module", "name": "util", "start_line": 1, "end_line": 1, "exported": true}, diff --git a/projectgraph/testdata/typescript/tsconfig-paths/indexes.json b/projectgraph/testdata/typescript/tsconfig-paths/indexes.json index cad611f1..90dedc99 100644 --- a/projectgraph/testdata/typescript/tsconfig-paths/indexes.json +++ b/projectgraph/testdata/typescript/tsconfig-paths/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "11", "path": "src/main.ts", "language": "typescript", + "version": "12", "path": "src/main.ts", "language": "typescript", "imports": [ {"line": 1, "module": "@app/server", "item": "serve", "alias": "serve", "kind": "static", "namespace": "@app//server//serve"}, {"line": 2, "module": "src/config", "item": "cfg", "alias": "cfg", "kind": "static", "namespace": "src//config//cfg"} @@ -12,7 +12,7 @@ ] }, { - "version": "11", "path": "src/app/server.ts", "language": "typescript", + "version": "12", "path": "src/app/server.ts", "language": "typescript", "exports": [{"name": "serve", "target": "src/app/server.ts//serve", "line": 1}], "definitions": [ {"key": "src/app/server.ts", "kind": "module", "name": "server", "start_line": 1, "end_line": 1, "exported": true}, @@ -20,7 +20,7 @@ ] }, { - "version": "11", "path": "src/config.ts", "language": "typescript", + "version": "12", "path": "src/config.ts", "language": "typescript", "exports": [{"name": "cfg", "target": "src/config.ts//cfg", "line": 1}], "definitions": [ {"key": "src/config.ts", "kind": "module", "name": "config", "start_line": 1, "end_line": 1, "exported": true}, diff --git a/projectgraph/testdata/typescript/type-only/indexes.json b/projectgraph/testdata/typescript/type-only/indexes.json index 9c493a32..2da7f73f 100644 --- a/projectgraph/testdata/typescript/type-only/indexes.json +++ b/projectgraph/testdata/typescript/type-only/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "11", "path": "src/main.ts", "language": "typescript", + "version": "12", "path": "src/main.ts", "language": "typescript", "imports": [ {"line": 1, "module": "./types", "item": "Opts", "alias": "Opts", "kind": "type-only", "namespace": ".//types//Opts"}, {"line": 2, "module": "./decl", "item": "decl", "alias": "decl", "kind": "static", "namespace": ".//decl//decl"} @@ -9,7 +9,7 @@ "calls": [{"caller": "src/main.ts", "line": 3, "column": 1, "callee": "decl", "targets": [".//decl//decl"], "kind": "call"}] }, { - "version": "11", "path": "src/types.ts", "language": "typescript", + "version": "12", "path": "src/types.ts", "language": "typescript", "exports": [{"name": "Opts", "target": "src/types.ts//Opts", "line": 1}], "definitions": [ {"key": "src/types.ts", "kind": "module", "name": "types", "start_line": 1, "end_line": 1, "exported": true}, @@ -17,7 +17,7 @@ ] }, { - "version": "11", "path": "src/decl.d.ts", "language": "typescript", + "version": "12", "path": "src/decl.d.ts", "language": "typescript", "exports": [{"name": "decl", "target": "src/decl.d.ts//decl", "line": 1}], "definitions": [ {"key": "src/decl.d.ts", "kind": "module", "name": "decl", "start_line": 1, "end_line": 1, "exported": true}, diff --git a/test/projectgraph/cases/go/dependency-build-files/expect.yaml b/test/projectgraph/cases/go/dependency-build-files/expect.yaml new file mode 100644 index 00000000..2f1d3c0f --- /dev/null +++ b/test/projectgraph/cases/go/dependency-build-files/expect.yaml @@ -0,0 +1,87 @@ +source: safedep:hand +description: >- + main calls Run of the unit github.com/acme/kit. No Go build reads two files of the unit, so the index step + leaves them out. gen.go has //go:build ignore and package main, and kit_test.go is a test file. So the link + step records no truncation for a second package clause, and their calls reach no graph. kit_windows.go has + //go:build windows and stays, because a reach answer must hold for every platform: its call of + syscall.CloseHandle is in the graph, and no root reaches it. tables.go is a generated file whose var holds a + string of 60 parts, past the depth limit of the index. Its index blanks the data value and keeps the body of + lookup, so the file is complete, gives no budget and keeps its calls. +units: + gomod/github.com/acme/kit@v1.0.0: go:github.com/acme/kit@v1.0.0 +roots: + - key: main.go + kind: main + rule: go.main + - key: main.go//main + kind: main + rule: go.main +exact_edges: true +edges: + - from: main.go//main + to: github.com//acme//kit//Run + kind: direct + confidence: high + site: main.go:6 + - from: github.com//acme//kit//Run + to: go:github.com/acme/kit@v1.0.0//kit.go//Run + kind: package + confidence: high + site: go:github.com/acme/kit@v1.0.0//kit.go:5 + - from: go:github.com/acme/kit@v1.0.0//kit.go//Run + to: os//exec//Command + kind: direct + confidence: high + site: go:github.com/acme/kit@v1.0.0//kit.go:6 + - from: go:github.com/acme/kit@v1.0.0//kit.go//Run + to: go:github.com/acme/kit@v1.0.0//tables.go//lookup + kind: direct + confidence: high + site: go:github.com/acme/kit@v1.0.0//kit.go:7 + - from: go:github.com/acme/kit@v1.0.0//kit_windows.go//Close + to: syscall//CloseHandle + kind: direct + confidence: high + site: go:github.com/acme/kit@v1.0.0//kit_windows.go:8 + - from: go:github.com/acme/kit@v1.0.0//tables.go//lookup + to: len + kind: direct + confidence: high + site: go:github.com/acme/kit@v1.0.0//tables.go:6 + - from: go:github.com/acme/kit@v1.0.0//tables.go//lookup + to: len + kind: direct + confidence: high + site: go:github.com/acme/kit@v1.0.0//tables.go:6 +unresolved: [] +results: + - target: os//exec//Command + status: found + path: + - key: main.go//main + - key: github.com//acme//kit//Run + site: main.go:6 + kind: direct + confidence: high + - key: go:github.com/acme/kit@v1.0.0//kit.go//Run + site: go:github.com/acme/kit@v1.0.0//kit.go:5 + kind: package + confidence: high + - key: os//exec//Command + site: go:github.com/acme/kit@v1.0.0//kit.go:6 + kind: direct + confidence: high + call_sites: + - caller: go:github.com/acme/kit@v1.0.0//kit.go//Run + site: go:github.com/acme/kit@v1.0.0//kit.go:6 + reached: true + - target: os//RemoveAll + status: no-such-target + - target: os//WriteFile + status: no-such-target + - target: syscall//CloseHandle + status: not-found + call_sites: + - caller: go:github.com/acme/kit@v1.0.0//kit_windows.go//Close + site: go:github.com/acme/kit@v1.0.0//kit_windows.go:8 + reached: false diff --git a/test/projectgraph/cases/go/dependency-build-files/project/go.mod b/test/projectgraph/cases/go/dependency-build-files/project/go.mod new file mode 100644 index 00000000..03907493 --- /dev/null +++ b/test/projectgraph/cases/go/dependency-build-files/project/go.mod @@ -0,0 +1,5 @@ +module example.com/app + +go 1.22 + +require github.com/acme/kit v1.0.0 diff --git a/test/projectgraph/cases/go/dependency-build-files/project/gomod/github.com/acme/kit@v1.0.0/gen.go b/test/projectgraph/cases/go/dependency-build-files/project/gomod/github.com/acme/kit@v1.0.0/gen.go new file mode 100644 index 00000000..c5f4217d --- /dev/null +++ b/test/projectgraph/cases/go/dependency-build-files/project/gomod/github.com/acme/kit@v1.0.0/gen.go @@ -0,0 +1,9 @@ +//go:build ignore + +package main + +import "os" + +func main() { + os.WriteFile("tables.go", nil, 0o644) +} diff --git a/test/projectgraph/cases/go/dependency-build-files/project/gomod/github.com/acme/kit@v1.0.0/go.mod b/test/projectgraph/cases/go/dependency-build-files/project/gomod/github.com/acme/kit@v1.0.0/go.mod new file mode 100644 index 00000000..529dd612 --- /dev/null +++ b/test/projectgraph/cases/go/dependency-build-files/project/gomod/github.com/acme/kit@v1.0.0/go.mod @@ -0,0 +1,3 @@ +module github.com/acme/kit + +go 1.22 diff --git a/test/projectgraph/cases/go/dependency-build-files/project/gomod/github.com/acme/kit@v1.0.0/kit.go b/test/projectgraph/cases/go/dependency-build-files/project/gomod/github.com/acme/kit@v1.0.0/kit.go new file mode 100644 index 00000000..d1844170 --- /dev/null +++ b/test/projectgraph/cases/go/dependency-build-files/project/gomod/github.com/acme/kit@v1.0.0/kit.go @@ -0,0 +1,8 @@ +package kit + +import "os/exec" + +func Run(name string) { + _ = exec.Command(name) + _ = lookup(name) +} diff --git a/test/projectgraph/cases/go/dependency-build-files/project/gomod/github.com/acme/kit@v1.0.0/kit_test.go b/test/projectgraph/cases/go/dependency-build-files/project/gomod/github.com/acme/kit@v1.0.0/kit_test.go new file mode 100644 index 00000000..e126828b --- /dev/null +++ b/test/projectgraph/cases/go/dependency-build-files/project/gomod/github.com/acme/kit@v1.0.0/kit_test.go @@ -0,0 +1,11 @@ +package kit + +import ( + "os" + "testing" +) + +func TestRun(t *testing.T) { + os.RemoveAll(t.TempDir()) + Run("a") +} diff --git a/test/projectgraph/cases/go/dependency-build-files/project/gomod/github.com/acme/kit@v1.0.0/kit_windows.go b/test/projectgraph/cases/go/dependency-build-files/project/gomod/github.com/acme/kit@v1.0.0/kit_windows.go new file mode 100644 index 00000000..51d664ed --- /dev/null +++ b/test/projectgraph/cases/go/dependency-build-files/project/gomod/github.com/acme/kit@v1.0.0/kit_windows.go @@ -0,0 +1,9 @@ +//go:build windows + +package kit + +import "syscall" + +func Close() { + syscall.CloseHandle(0) +} diff --git a/test/projectgraph/cases/go/dependency-build-files/project/gomod/github.com/acme/kit@v1.0.0/tables.go b/test/projectgraph/cases/go/dependency-build-files/project/gomod/github.com/acme/kit@v1.0.0/tables.go new file mode 100644 index 00000000..54d6e94c --- /dev/null +++ b/test/projectgraph/cases/go/dependency-build-files/project/gomod/github.com/acme/kit@v1.0.0/tables.go @@ -0,0 +1,9 @@ +// Code generated by gen.go. DO NOT EDIT. + +package kit + +func lookup(name string) int { + return len(names) + len(name) +} + +var names string = "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" diff --git a/test/projectgraph/cases/go/dependency-build-files/project/main.go b/test/projectgraph/cases/go/dependency-build-files/project/main.go new file mode 100644 index 00000000..78434045 --- /dev/null +++ b/test/projectgraph/cases/go/dependency-build-files/project/main.go @@ -0,0 +1,7 @@ +package main + +import "github.com/acme/kit" + +func main() { + kit.Run("a") +} diff --git a/test/projectgraph/cases/go/dependency-build-files/summaries/go_github.com_acme_kit_v1.0.0.json b/test/projectgraph/cases/go/dependency-build-files/summaries/go_github.com_acme_kit_v1.0.0.json new file mode 100644 index 00000000..15b1ed48 --- /dev/null +++ b/test/projectgraph/cases/go/dependency-build-files/summaries/go_github.com_acme_kit_v1.0.0.json @@ -0,0 +1,30 @@ +{ + "version": "2", + "index_version": "12", + "link_version": "12", + "unit": "go:github.com/acme/kit@v1.0.0", + "symbols": [ + { + "key": "github.com//acme//kit//Close", + "definitions": [ + "go:github.com/acme/kit@v1.0.0//kit_windows.go//Close" + ], + "reaches": [ + "syscall//CloseHandle" + ] + }, + { + "key": "github.com//acme//kit//Run", + "definitions": [ + "go:github.com/acme/kit@v1.0.0//kit.go//Run" + ], + "reaches": [ + "os//exec//Command" + ] + } + ], + "targets": [ + "os//exec//Command", + "syscall//CloseHandle" + ] +} diff --git a/test/projectgraph/cases/go/dependency-generated-code/expect.yaml b/test/projectgraph/cases/go/dependency-generated-code/expect.yaml new file mode 100644 index 00000000..f2a52f83 --- /dev/null +++ b/test/projectgraph/cases/go/dependency-generated-code/expect.yaml @@ -0,0 +1,41 @@ +source: safedep:hand +description: >- + main calls Run of the unit, and Run calls gen in gen.go, a generated file. Its var holds a string of 60 + parts, past the depth limit of the index. The index blanks the data value and keeps the body of gen, so the + call of exec.Command stays in the graph and reach finds it. A skeleton that dropped the bodies gave + no-such-target. +units: + gomod/github.com/acme/kit@v1.0.0: go:github.com/acme/kit@v1.0.0 +roots: + - key: main.go + kind: main + rule: go.main + - key: main.go//main + kind: main + rule: go.main +unresolved: [] +results: + - target: os//exec//Command + status: found + path: + - key: main.go//main + - key: github.com//acme//kit//Run + site: main.go:6 + kind: direct + confidence: high + - key: go:github.com/acme/kit@v1.0.0//kit.go//Run + site: go:github.com/acme/kit@v1.0.0//kit.go:3 + kind: package + confidence: high + - key: go:github.com/acme/kit@v1.0.0//gen.go//gen + site: go:github.com/acme/kit@v1.0.0//kit.go:3 + kind: direct + confidence: high + - key: os//exec//Command + site: go:github.com/acme/kit@v1.0.0//gen.go:7 + kind: direct + confidence: high + call_sites: + - caller: go:github.com/acme/kit@v1.0.0//gen.go//gen + site: go:github.com/acme/kit@v1.0.0//gen.go:7 + reached: true diff --git a/test/projectgraph/cases/go/dependency-generated-code/project/go.mod b/test/projectgraph/cases/go/dependency-generated-code/project/go.mod new file mode 100644 index 00000000..c7090c9c --- /dev/null +++ b/test/projectgraph/cases/go/dependency-generated-code/project/go.mod @@ -0,0 +1,5 @@ +module app + +go 1.22 + +require github.com/acme/kit v1.0.0 diff --git a/test/projectgraph/cases/go/dependency-generated-code/project/gomod/github.com/acme/kit@v1.0.0/gen.go b/test/projectgraph/cases/go/dependency-generated-code/project/gomod/github.com/acme/kit@v1.0.0/gen.go new file mode 100644 index 00000000..6f91564d --- /dev/null +++ b/test/projectgraph/cases/go/dependency-generated-code/project/gomod/github.com/acme/kit@v1.0.0/gen.go @@ -0,0 +1,9 @@ +// Code generated by synth. DO NOT EDIT. + +package kit + +import "os/exec" + +func gen() { _ = exec.Command("x") } + +var data string = "" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" + "x" diff --git a/test/projectgraph/cases/go/dependency-generated-code/project/gomod/github.com/acme/kit@v1.0.0/go.mod b/test/projectgraph/cases/go/dependency-generated-code/project/gomod/github.com/acme/kit@v1.0.0/go.mod new file mode 100644 index 00000000..529dd612 --- /dev/null +++ b/test/projectgraph/cases/go/dependency-generated-code/project/gomod/github.com/acme/kit@v1.0.0/go.mod @@ -0,0 +1,3 @@ +module github.com/acme/kit + +go 1.22 diff --git a/test/projectgraph/cases/go/dependency-generated-code/project/gomod/github.com/acme/kit@v1.0.0/kit.go b/test/projectgraph/cases/go/dependency-generated-code/project/gomod/github.com/acme/kit@v1.0.0/kit.go new file mode 100644 index 00000000..8e7a76b3 --- /dev/null +++ b/test/projectgraph/cases/go/dependency-generated-code/project/gomod/github.com/acme/kit@v1.0.0/kit.go @@ -0,0 +1,3 @@ +package kit + +func Run() { gen() } diff --git a/test/projectgraph/cases/go/dependency-generated-code/project/main.go b/test/projectgraph/cases/go/dependency-generated-code/project/main.go new file mode 100644 index 00000000..fc95b081 --- /dev/null +++ b/test/projectgraph/cases/go/dependency-generated-code/project/main.go @@ -0,0 +1,7 @@ +package main + +import "github.com/acme/kit" + +func main() { + kit.Run() +} diff --git a/test/projectgraph/cases/go/dependency-generated-code/summaries/go_github.com_acme_kit_v1.0.0.json b/test/projectgraph/cases/go/dependency-generated-code/summaries/go_github.com_acme_kit_v1.0.0.json new file mode 100644 index 00000000..5ec9339d --- /dev/null +++ b/test/projectgraph/cases/go/dependency-generated-code/summaries/go_github.com_acme_kit_v1.0.0.json @@ -0,0 +1,20 @@ +{ + "version": "2", + "index_version": "12", + "link_version": "12", + "unit": "go:github.com/acme/kit@v1.0.0", + "symbols": [ + { + "key": "github.com//acme//kit//Run", + "definitions": [ + "go:github.com/acme/kit@v1.0.0//kit.go//Run" + ], + "reaches": [ + "os//exec//Command" + ] + } + ], + "targets": [ + "os//exec//Command" + ] +} diff --git a/test/projectgraph/cases/go/dependency-implements-interface/summaries/go_github.com_acme_dep_v1.0.0.json b/test/projectgraph/cases/go/dependency-implements-interface/summaries/go_github.com_acme_dep_v1.0.0.json index 8836d4ff..00e7c63f 100644 --- a/test/projectgraph/cases/go/dependency-implements-interface/summaries/go_github.com_acme_dep_v1.0.0.json +++ b/test/projectgraph/cases/go/dependency-implements-interface/summaries/go_github.com_acme_dep_v1.0.0.json @@ -1,6 +1,6 @@ { "version": "2", - "index_version": "11", + "index_version": "12", "link_version": "12", "unit": "go:github.com/acme/dep@v1.0.0", "symbols": [ diff --git a/test/projectgraph/cases/go/dependency-module/summaries/go_github.com_acme_yamlish_v1.2.0.json b/test/projectgraph/cases/go/dependency-module/summaries/go_github.com_acme_yamlish_v1.2.0.json index 1de719a9..f4f1cdd7 100644 --- a/test/projectgraph/cases/go/dependency-module/summaries/go_github.com_acme_yamlish_v1.2.0.json +++ b/test/projectgraph/cases/go/dependency-module/summaries/go_github.com_acme_yamlish_v1.2.0.json @@ -1,6 +1,6 @@ { "version": "2", - "index_version": "11", + "index_version": "12", "link_version": "12", "unit": "go:github.com/acme/yamlish@v1.2.0", "symbols": [ diff --git a/test/projectgraph/cases/go/dependency-platform-files/expect.yaml b/test/projectgraph/cases/go/dependency-platform-files/expect.yaml new file mode 100644 index 00000000..7b80a2c5 --- /dev/null +++ b/test/projectgraph/cases/go/dependency-platform-files/expect.yaml @@ -0,0 +1,40 @@ +source: safedep:hand +description: >- + main calls Run of the unit, and Run calls platform. kit_linux.go and kit_darwin.go each define platform, + and only the darwin twin calls exec.Command. The index keeps both twins, because a reach answer must hold + for every platform, so reach finds the call. An index of linux/amd64 alone gave no-such-target. +units: + gomod/github.com/acme/kit@v1.0.0: go:github.com/acme/kit@v1.0.0 +roots: + - key: main.go + kind: main + rule: go.main + - key: main.go//main + kind: main + rule: go.main +unresolved: [] +results: + - target: os//exec//Command + status: found + path: + - key: main.go//main + - key: github.com//acme//kit//Run + site: main.go:6 + kind: direct + confidence: high + - key: go:github.com/acme/kit@v1.0.0//kit.go//Run + site: go:github.com/acme/kit@v1.0.0//kit.go:3 + kind: package + confidence: high + - key: go:github.com/acme/kit@v1.0.0//kit_darwin.go//platform + site: go:github.com/acme/kit@v1.0.0//kit.go:3 + kind: direct + confidence: high + - key: os//exec//Command + site: go:github.com/acme/kit@v1.0.0//kit_darwin.go:5 + kind: direct + confidence: high + call_sites: + - caller: go:github.com/acme/kit@v1.0.0//kit_darwin.go//platform + site: go:github.com/acme/kit@v1.0.0//kit_darwin.go:5 + reached: true diff --git a/test/projectgraph/cases/go/dependency-platform-files/project/go.mod b/test/projectgraph/cases/go/dependency-platform-files/project/go.mod new file mode 100644 index 00000000..c7090c9c --- /dev/null +++ b/test/projectgraph/cases/go/dependency-platform-files/project/go.mod @@ -0,0 +1,5 @@ +module app + +go 1.22 + +require github.com/acme/kit v1.0.0 diff --git a/test/projectgraph/cases/go/dependency-platform-files/project/gomod/github.com/acme/kit@v1.0.0/go.mod b/test/projectgraph/cases/go/dependency-platform-files/project/gomod/github.com/acme/kit@v1.0.0/go.mod new file mode 100644 index 00000000..529dd612 --- /dev/null +++ b/test/projectgraph/cases/go/dependency-platform-files/project/gomod/github.com/acme/kit@v1.0.0/go.mod @@ -0,0 +1,3 @@ +module github.com/acme/kit + +go 1.22 diff --git a/test/projectgraph/cases/go/dependency-platform-files/project/gomod/github.com/acme/kit@v1.0.0/kit.go b/test/projectgraph/cases/go/dependency-platform-files/project/gomod/github.com/acme/kit@v1.0.0/kit.go new file mode 100644 index 00000000..de7427c6 --- /dev/null +++ b/test/projectgraph/cases/go/dependency-platform-files/project/gomod/github.com/acme/kit@v1.0.0/kit.go @@ -0,0 +1,3 @@ +package kit + +func Run() { platform() } diff --git a/test/projectgraph/cases/go/dependency-platform-files/project/gomod/github.com/acme/kit@v1.0.0/kit_darwin.go b/test/projectgraph/cases/go/dependency-platform-files/project/gomod/github.com/acme/kit@v1.0.0/kit_darwin.go new file mode 100644 index 00000000..cd40708f --- /dev/null +++ b/test/projectgraph/cases/go/dependency-platform-files/project/gomod/github.com/acme/kit@v1.0.0/kit_darwin.go @@ -0,0 +1,5 @@ +package kit + +import "os/exec" + +func platform() { _ = exec.Command("x") } diff --git a/test/projectgraph/cases/go/dependency-platform-files/project/gomod/github.com/acme/kit@v1.0.0/kit_linux.go b/test/projectgraph/cases/go/dependency-platform-files/project/gomod/github.com/acme/kit@v1.0.0/kit_linux.go new file mode 100644 index 00000000..22509de1 --- /dev/null +++ b/test/projectgraph/cases/go/dependency-platform-files/project/gomod/github.com/acme/kit@v1.0.0/kit_linux.go @@ -0,0 +1,3 @@ +package kit + +func platform() {} diff --git a/test/projectgraph/cases/go/dependency-platform-files/project/main.go b/test/projectgraph/cases/go/dependency-platform-files/project/main.go new file mode 100644 index 00000000..fc95b081 --- /dev/null +++ b/test/projectgraph/cases/go/dependency-platform-files/project/main.go @@ -0,0 +1,7 @@ +package main + +import "github.com/acme/kit" + +func main() { + kit.Run() +} diff --git a/test/projectgraph/cases/go/dependency-platform-files/summaries/go_github.com_acme_kit_v1.0.0.json b/test/projectgraph/cases/go/dependency-platform-files/summaries/go_github.com_acme_kit_v1.0.0.json new file mode 100644 index 00000000..5ec9339d --- /dev/null +++ b/test/projectgraph/cases/go/dependency-platform-files/summaries/go_github.com_acme_kit_v1.0.0.json @@ -0,0 +1,20 @@ +{ + "version": "2", + "index_version": "12", + "link_version": "12", + "unit": "go:github.com/acme/kit@v1.0.0", + "symbols": [ + { + "key": "github.com//acme//kit//Run", + "definitions": [ + "go:github.com/acme/kit@v1.0.0//kit.go//Run" + ], + "reaches": [ + "os//exec//Command" + ] + } + ], + "targets": [ + "os//exec//Command" + ] +} diff --git a/test/projectgraph/cases/javascript/dependency-candidates/summaries/npm_store-kit_1.0.0.json b/test/projectgraph/cases/javascript/dependency-candidates/summaries/npm_store-kit_1.0.0.json index 7d682070..2d0fc5dc 100644 --- a/test/projectgraph/cases/javascript/dependency-candidates/summaries/npm_store-kit_1.0.0.json +++ b/test/projectgraph/cases/javascript/dependency-candidates/summaries/npm_store-kit_1.0.0.json @@ -1,6 +1,6 @@ { "version": "2", - "index_version": "11", + "index_version": "12", "link_version": "12", "unit": "npm:store-kit@1.0.0", "symbols": [ diff --git a/test/projectgraph/cases/javascript/dependency-exports-import/summaries/npm_kit_1.0.0.json b/test/projectgraph/cases/javascript/dependency-exports-import/summaries/npm_kit_1.0.0.json index 16f7e673..21bdbc6a 100644 --- a/test/projectgraph/cases/javascript/dependency-exports-import/summaries/npm_kit_1.0.0.json +++ b/test/projectgraph/cases/javascript/dependency-exports-import/summaries/npm_kit_1.0.0.json @@ -1,6 +1,6 @@ { "version": "2", - "index_version": "11", + "index_version": "12", "link_version": "12", "unit": "npm:kit@1.0.0", "symbols": [ diff --git a/test/projectgraph/cases/javascript/dependency-exports-require/summaries/npm_kit_1.0.0.json b/test/projectgraph/cases/javascript/dependency-exports-require/summaries/npm_kit_1.0.0.json index 16f7e673..21bdbc6a 100644 --- a/test/projectgraph/cases/javascript/dependency-exports-require/summaries/npm_kit_1.0.0.json +++ b/test/projectgraph/cases/javascript/dependency-exports-require/summaries/npm_kit_1.0.0.json @@ -1,6 +1,6 @@ { "version": "2", - "index_version": "11", + "index_version": "12", "link_version": "12", "unit": "npm:kit@1.0.0", "symbols": [ diff --git a/test/projectgraph/cases/javascript/dependency-method-name/summaries/npm_plug_1.0.0.json b/test/projectgraph/cases/javascript/dependency-method-name/summaries/npm_plug_1.0.0.json index 7c8c9b59..49f531f9 100644 --- a/test/projectgraph/cases/javascript/dependency-method-name/summaries/npm_plug_1.0.0.json +++ b/test/projectgraph/cases/javascript/dependency-method-name/summaries/npm_plug_1.0.0.json @@ -1,6 +1,6 @@ { "version": "2", - "index_version": "11", + "index_version": "12", "link_version": "12", "unit": "npm:plug@1.0.0", "symbols": [ diff --git a/test/projectgraph/cases/javascript/dependency-same-path/summaries/npm_util-kit_1.0.0.json b/test/projectgraph/cases/javascript/dependency-same-path/summaries/npm_util-kit_1.0.0.json index 539bf522..fd462e57 100644 --- a/test/projectgraph/cases/javascript/dependency-same-path/summaries/npm_util-kit_1.0.0.json +++ b/test/projectgraph/cases/javascript/dependency-same-path/summaries/npm_util-kit_1.0.0.json @@ -1,6 +1,6 @@ { "version": "2", - "index_version": "11", + "index_version": "12", "link_version": "12", "unit": "npm:util-kit@1.0.0", "symbols": [ diff --git a/test/projectgraph/cases/javascript/dependency-transitive/summaries/npm_lodash._reinterpolate_3.0.0.json b/test/projectgraph/cases/javascript/dependency-transitive/summaries/npm_lodash._reinterpolate_3.0.0.json index f7ddda0c..5990e0b2 100644 --- a/test/projectgraph/cases/javascript/dependency-transitive/summaries/npm_lodash._reinterpolate_3.0.0.json +++ b/test/projectgraph/cases/javascript/dependency-transitive/summaries/npm_lodash._reinterpolate_3.0.0.json @@ -1,6 +1,6 @@ { "version": "2", - "index_version": "11", + "index_version": "12", "link_version": "12", "unit": "npm:lodash._reinterpolate@3.0.0", "symbols": [ diff --git a/test/projectgraph/cases/javascript/dependency-transitive/summaries/npm_lodash_4.17.21.json b/test/projectgraph/cases/javascript/dependency-transitive/summaries/npm_lodash_4.17.21.json index 4ac7ee48..f3662300 100644 --- a/test/projectgraph/cases/javascript/dependency-transitive/summaries/npm_lodash_4.17.21.json +++ b/test/projectgraph/cases/javascript/dependency-transitive/summaries/npm_lodash_4.17.21.json @@ -1,6 +1,6 @@ { "version": "2", - "index_version": "11", + "index_version": "12", "link_version": "12", "unit": "npm:lodash@4.17.21", "symbols": [ diff --git a/test/projectgraph/cases/python/dependency-dark-unit/summaries/pypi_PyYAML_6.0.1.json b/test/projectgraph/cases/python/dependency-dark-unit/summaries/pypi_PyYAML_6.0.1.json index 61db2c73..403e9132 100644 --- a/test/projectgraph/cases/python/dependency-dark-unit/summaries/pypi_PyYAML_6.0.1.json +++ b/test/projectgraph/cases/python/dependency-dark-unit/summaries/pypi_PyYAML_6.0.1.json @@ -1,6 +1,6 @@ { "version": "2", - "index_version": "11", + "index_version": "12", "link_version": "12", "unit": "pypi:PyYAML@6.0.1", "symbols": [ diff --git a/test/projectgraph/cases/python/dependency-failed-reached/summaries/pypi_badpkg_1.0.0.json b/test/projectgraph/cases/python/dependency-failed-reached/summaries/pypi_badpkg_1.0.0.json index 8d4b6622..8a516a1e 100644 --- a/test/projectgraph/cases/python/dependency-failed-reached/summaries/pypi_badpkg_1.0.0.json +++ b/test/projectgraph/cases/python/dependency-failed-reached/summaries/pypi_badpkg_1.0.0.json @@ -1,6 +1,6 @@ { "version": "2", - "index_version": "11", + "index_version": "12", "link_version": "12", "unit": "pypi:badpkg@1.0.0", "symbols": [ diff --git a/test/projectgraph/cases/python/dependency-failed-reached/summaries/pypi_okpkg_1.0.0.json b/test/projectgraph/cases/python/dependency-failed-reached/summaries/pypi_okpkg_1.0.0.json index fb663fca..0f95caeb 100644 --- a/test/projectgraph/cases/python/dependency-failed-reached/summaries/pypi_okpkg_1.0.0.json +++ b/test/projectgraph/cases/python/dependency-failed-reached/summaries/pypi_okpkg_1.0.0.json @@ -1,6 +1,6 @@ { "version": "2", - "index_version": "11", + "index_version": "12", "link_version": "12", "unit": "pypi:okpkg@1.0.0", "symbols": [ diff --git a/test/projectgraph/cases/python/dependency-failed-unreached/summaries/pypi_badpkg_1.0.0.json b/test/projectgraph/cases/python/dependency-failed-unreached/summaries/pypi_badpkg_1.0.0.json index 8d4b6622..8a516a1e 100644 --- a/test/projectgraph/cases/python/dependency-failed-unreached/summaries/pypi_badpkg_1.0.0.json +++ b/test/projectgraph/cases/python/dependency-failed-unreached/summaries/pypi_badpkg_1.0.0.json @@ -1,6 +1,6 @@ { "version": "2", - "index_version": "11", + "index_version": "12", "link_version": "12", "unit": "pypi:badpkg@1.0.0", "symbols": [ diff --git a/test/projectgraph/cases/python/dependency-failed-unreached/summaries/pypi_okpkg_1.0.0.json b/test/projectgraph/cases/python/dependency-failed-unreached/summaries/pypi_okpkg_1.0.0.json index fb663fca..0f95caeb 100644 --- a/test/projectgraph/cases/python/dependency-failed-unreached/summaries/pypi_okpkg_1.0.0.json +++ b/test/projectgraph/cases/python/dependency-failed-unreached/summaries/pypi_okpkg_1.0.0.json @@ -1,6 +1,6 @@ { "version": "2", - "index_version": "11", + "index_version": "12", "link_version": "12", "unit": "pypi:okpkg@1.0.0", "symbols": [ diff --git a/test/projectgraph/cases/python/dependency-package-dir/summaries/pypi_outer_1.0.0.json b/test/projectgraph/cases/python/dependency-package-dir/summaries/pypi_outer_1.0.0.json index 53b1a049..3bdc373c 100644 --- a/test/projectgraph/cases/python/dependency-package-dir/summaries/pypi_outer_1.0.0.json +++ b/test/projectgraph/cases/python/dependency-package-dir/summaries/pypi_outer_1.0.0.json @@ -1,6 +1,6 @@ { "version": "2", - "index_version": "11", + "index_version": "12", "link_version": "12", "unit": "pypi:outer@1.0.0", "symbols": [ diff --git a/test/projectgraph/cases/python/dependency-site-packages/summaries/pypi_requests_2.31.0.json b/test/projectgraph/cases/python/dependency-site-packages/summaries/pypi_requests_2.31.0.json index 5fb351aa..df2da9d3 100644 --- a/test/projectgraph/cases/python/dependency-site-packages/summaries/pypi_requests_2.31.0.json +++ b/test/projectgraph/cases/python/dependency-site-packages/summaries/pypi_requests_2.31.0.json @@ -1,6 +1,6 @@ { "version": "2", - "index_version": "11", + "index_version": "12", "link_version": "12", "unit": "pypi:requests@2.31.0", "symbols": [ diff --git a/test/projectgraph/cases/python/dependency-site-packages/summaries/pypi_urllib3_2.0.7.json b/test/projectgraph/cases/python/dependency-site-packages/summaries/pypi_urllib3_2.0.7.json index 29327fa3..351b03cc 100644 --- a/test/projectgraph/cases/python/dependency-site-packages/summaries/pypi_urllib3_2.0.7.json +++ b/test/projectgraph/cases/python/dependency-site-packages/summaries/pypi_urllib3_2.0.7.json @@ -1,6 +1,6 @@ { "version": "2", - "index_version": "11", + "index_version": "12", "link_version": "12", "unit": "pypi:urllib3@2.0.7", "symbols": [ diff --git a/test/projectgraph/version_test.go b/test/projectgraph/version_test.go index 7b920dae..48d4c936 100644 --- a/test/projectgraph/version_test.go +++ b/test/projectgraph/version_test.go @@ -26,6 +26,7 @@ var graphHashes = map[string]string{ "11/10": "421c6441093c8f82e4db8a2342884b5e42faf360951985d3600d99c96202625b", "11/11": "19e0527969cf2702f5abfab8234310b254ac25a6a539ab6e86cf6febabfb8757", "11/12": "14d5bbbaca07ab950f75c665f48e71ce9195da2ebd853cd1b0b745d42c4852cc", + "12/12": "1cb170ec83fe3517e6f71755983c040210f83bb7a0e69528b3d7007dcc69d5dc", } // resultHashes records the hash of the reach results of every case for @@ -36,6 +37,7 @@ var resultHashes = map[string]string{ "11/11/1": "4beb6248ef1b8dc3572939a41d1aea11c6504f5007a5f24fd1261db026708186", "11/11/2": "58d8fd9c5b67bf02a44778a5173828515c9424a1fddd9ff3f644d8fc13ebcfbb", "11/12/2": "8341f2a1965f9d3b19d976ff1ba63ed26c1e15026e02079ec6195dfc0d762dba", + "12/12/2": "b89a142078d885eeb73fe15c731315c60d42b38270efd12a7857be44b9ccc83c", } // summaryHashes records the hash of the golden summaries of the cases for @@ -47,6 +49,7 @@ var summaryHashes = map[string]string{ "11/10/2": "8047cc4e4d95ce6744993a20c7f4224a78b2d3531c2e31415f371b16608f19a5", "11/11/2": "c0aeb7f515b2c546fd6b355663d5a7bb446c7f6bdfb9ab3ef343dd252e4302bf", "11/12/2": "f4102bd40a876fa387f7ff1ce81505e1f04b64cd8165602d0726f897e2b88a7f", + "12/12/2": "cb2147fda018110f38254519bbc75a814b99e0318f5c7485fbce78a667bb4287", } func TestGraphHashFollowsVersion(t *testing.T) { From 0ab63c53163a00ca66513a38e2804451d5d429c2 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 19 Oct 2026 08:12:33 +0000 Subject: [PATCH 5/9] feat(projectgraph): bind the receiver from Go result types of external functions and methods (FOU-543) z := html.NewTokenizer(r); z.Next() gave uncertain with unknown-receiver even with the code of x/net as a unit. The link step read the result type only of a first-party function, and only its first result. The Go index records the type of each result by position, in Results, and each name of an assignment or a short variable declaration that one call binds, with the position in Typed.Index. The blank name binds nothing. The link step reads the definition of a function in the project or in the unit that gives its package, and binds the type of the result to the name. A type of a unit becomes its external key, as golang.org//x//net//html//Tokenizer, so the call gets the key that a target names, and a package edge joins the key to the definition. A walk through an external key takes the result type of each function or method of a unit in turn, so pkg.F().M() and a.B().C() resolve. An interface result keeps the cha edges, and an implementation in a unit gets its edge through its external key. With no implementation the record has the new reason interface-receiver. A result of a package that no unit gives has the new reason no-dependency-source, and so has the record of a call on a call result from FOU-546. The standard library is a unit too: GOROOT/src as go:std@ gives each import path whose first element has no dot. To make that unit usable, the index step now also leaves out the files of a Go unit that are not Go source, the files under testdata and under a directory that starts with _ or ., and the cmd module of the std unit. The skeleton of a generated table blanks const values too, and an older DO NOT EDIT line or a name that starts with table marks such a file. The real GOROOT then links with no truncation, and http.NewRequest followed by req.Write finds net//http//Request//Write. The Go rows of the receiver table now pass. The case go/dependency-result-types checks the forms against a unit and a std unit, and go/dependency-implements-interface now finds Impl.Run by its external key. report.md has the numbers after this change. The govulncheck set still finds 9 of 9 stacks, and pycg-micro keeps 0.954. fileindex.Version is 13 and projectgraph.Version is 13. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01PFQbqPqd9xn1fT6hBsyoUm Signed-off-by: Claude --- docs/projectgraph.md | 34 ++- examples/projectgraph/main.go | 5 +- fileindex/index.go | 21 +- fileindex/testdata/index.json | 2 +- plugin/indexer/golang.go | 45 +++- plugin/indexer/golang_test.go | 9 + plugin/indexer/gounit.go | 54 ++++- plugin/indexer/gounit_test.go | 18 ++ plugin/indexer/indexer.go | 14 +- plugin/indexer/plugin_test.go | 1 + plugin/indexer/testdata/golden/App.cs.json | 2 +- plugin/indexer/testdata/golden/App.java.json | 2 +- plugin/indexer/testdata/golden/App.kt.json | 2 +- plugin/indexer/testdata/golden/app.go.json | 14 +- plugin/indexer/testdata/golden/app.js.json | 2 +- plugin/indexer/testdata/golden/app.php.json | 2 +- plugin/indexer/testdata/golden/app.py.json | 2 +- plugin/indexer/testdata/golden/app.rb.json | 2 +- plugin/indexer/testdata/golden/app.rs.json | 2 +- plugin/indexer/testdata/golden/app.ts.json | 2 +- .../indexer/testdata/golden/exports.js.json | 2 +- .../indexer/testdata/golden/exports.py.json | 2 +- .../indexer/testdata/golden/exports.ts.json | 2 +- plugin/indexer/testdata/golden/flags.js.json | 2 +- plugin/indexer/testdata/golden/flags.py.json | 2 +- plugin/indexer/testdata/golden/flags.ts.json | 2 +- projectgraph/declared.go | 40 ++-- projectgraph/golang.go | 28 ++- projectgraph/graph.go | 2 +- projectgraph/link.go | 7 +- projectgraph/modules/golang.go | 19 +- projectgraph/modules/golang_test.go | 20 ++ projectgraph/profile.go | 5 + projectgraph/resolve.go | 70 ++++-- projectgraph/results.go | 184 +++++++++++++++ .../javascript/barrel-chain/graph.golden.json | 2 +- .../javascript/barrel-chain/indexes.json | 8 +- .../commonjs-require/graph.golden.json | 2 +- .../javascript/commonjs-require/indexes.json | 6 +- .../default-import/graph.golden.json | 2 +- .../javascript/default-import/indexes.json | 6 +- .../esm-named-import/graph.golden.json | 2 +- .../javascript/esm-named-import/indexes.json | 6 +- .../javascript/export-star/graph.golden.json | 2 +- .../javascript/export-star/indexes.json | 10 +- .../exports-conditions/graph.golden.json | 2 +- .../exports-conditions/indexes.json | 8 +- .../unknown-receiver/graph.golden.json | 2 +- .../javascript/unknown-receiver/indexes.json | 2 +- .../javascript/workspaces/graph.golden.json | 2 +- .../javascript/workspaces/indexes.json | 6 +- .../python/all-hides/graph.golden.json | 2 +- .../testdata/python/all-hides/indexes.json | 6 +- .../python/assignment/graph.golden.json | 2 +- .../testdata/python/assignment/indexes.json | 2 +- .../python/class-body/graph.golden.json | 2 +- .../testdata/python/class-body/indexes.json | 2 +- .../python/decorators/graph.golden.json | 2 +- .../testdata/python/decorators/indexes.json | 2 +- .../python/external/graph.golden.json | 2 +- .../testdata/python/external/indexes.json | 2 +- .../python/local-receiver/graph.golden.json | 2 +- .../python/local-receiver/indexes.json | 2 +- .../python/member-call/graph.golden.json | 2 +- .../testdata/python/member-call/indexes.json | 6 +- .../python/missing-file/graph.golden.json | 2 +- .../testdata/python/missing-file/indexes.json | 2 +- .../python/package-init/graph.golden.json | 2 +- .../testdata/python/package-init/indexes.json | 6 +- .../python/reexport-cycle/graph.golden.json | 2 +- .../python/reexport-cycle/indexes.json | 6 +- .../python/relative-imports/graph.golden.json | 2 +- .../python/relative-imports/indexes.json | 6 +- .../python/sibling-import/graph.golden.json | 2 +- .../python/sibling-import/indexes.json | 4 +- .../python/src-layout/graph.golden.json | 2 +- .../testdata/python/src-layout/indexes.json | 6 +- .../python/sys-path/graph.golden.json | 2 +- .../testdata/python/sys-path/indexes.json | 4 +- .../python/wildcard/graph.golden.json | 2 +- .../testdata/python/wildcard/indexes.json | 4 +- .../roots/extra-rules/graph.golden.json | 2 +- .../testdata/roots/extra-rules/indexes.json | 6 +- .../roots/library-surface/graph.golden.json | 2 +- .../roots/library-surface/indexes.json | 4 +- .../roots/main-guard/graph.golden.json | 2 +- .../testdata/roots/main-guard/indexes.json | 4 +- .../roots/package-json-bin/graph.golden.json | 2 +- .../roots/package-json-bin/indexes.json | 4 +- .../roots/package-json-main/graph.golden.json | 2 +- .../roots/package-json-main/indexes.json | 8 +- .../procfile-dockerfile/graph.golden.json | 2 +- .../roots/procfile-dockerfile/indexes.json | 12 +- .../roots/pyproject-scripts/graph.golden.json | 2 +- .../roots/pyproject-scripts/indexes.json | 10 +- .../testdata/roots/setup-py/graph.golden.json | 2 +- .../testdata/roots/setup-py/indexes.json | 4 +- .../roots/test-file/graph.golden.json | 2 +- .../testdata/roots/test-file/indexes.json | 2 +- .../typescript/js-specifier/graph.golden.json | 2 +- .../typescript/js-specifier/indexes.json | 4 +- .../tsconfig-paths/graph.golden.json | 2 +- .../typescript/tsconfig-paths/indexes.json | 6 +- .../typescript/type-only/graph.golden.json | 2 +- .../typescript/type-only/indexes.json | 6 +- projectgraph/walk.go | 15 +- .../go/chained-external-call/expect.yaml | 5 +- .../go_github.com_acme_kit_v1.0.0.json | 4 +- .../go_github.com_acme_kit_v1.0.0.json | 4 +- .../expect.yaml | 29 ++- .../go_github.com_acme_dep_v1.0.0.json | 4 +- .../go_github.com_acme_yamlish_v1.2.0.json | 4 +- .../go_github.com_acme_kit_v1.0.0.json | 4 +- .../go/dependency-result-types/expect.yaml | 218 ++++++++++++++++++ .../go/dependency-result-types/project/go.mod | 5 + .../gomod/example.com/markup@v1.0.0/go.mod | 3 + .../gomod/example.com/markup@v1.0.0/node.go | 16 ++ .../gomod/example.com/markup@v1.0.0/token.go | 29 +++ .../project/goroot/src/go.mod | 3 + .../project/goroot/src/io/io.go | 9 + .../project/goroot/src/net/http/request.go | 17 ++ .../project/goroot/src/os/exec/exec.go | 7 + .../project/goroot/src/os/file.go | 9 + .../dependency-result-types/project/main.go | 32 +++ .../go_example.com_markup_v1.0.0.json | 81 +++++++ .../summaries/go_std_go1.25.1.json | 72 ++++++ .../cases/go/interface-method-set/expect.yaml | 4 +- .../javascript/commander-action/expect.yaml | 4 +- .../summaries/npm_store-kit_1.0.0.json | 4 +- .../summaries/npm_kit_1.0.0.json | 4 +- .../summaries/npm_kit_1.0.0.json | 4 +- .../summaries/npm_plug_1.0.0.json | 4 +- .../summaries/npm_util-kit_1.0.0.json | 4 +- .../npm_lodash._reinterpolate_3.0.0.json | 4 +- .../summaries/npm_lodash_4.17.21.json | 4 +- .../references-argument/expect.yaml | 4 +- .../summaries/pypi_PyYAML_6.0.1.json | 4 +- .../summaries/pypi_badpkg_1.0.0.json | 4 +- .../summaries/pypi_okpkg_1.0.0.json | 4 +- .../summaries/pypi_badpkg_1.0.0.json | 4 +- .../summaries/pypi_okpkg_1.0.0.json | 4 +- .../summaries/pypi_outer_1.0.0.json | 4 +- .../summaries/pypi_requests_2.31.0.json | 4 +- .../summaries/pypi_urllib3_2.0.7.json | 4 +- test/projectgraph/receivers_test.go | 14 -- test/projectgraph/report.md | 64 +++-- test/projectgraph/version_test.go | 3 + 147 files changed, 1284 insertions(+), 313 deletions(-) create mode 100644 projectgraph/results.go create mode 100644 test/projectgraph/cases/go/dependency-result-types/expect.yaml create mode 100644 test/projectgraph/cases/go/dependency-result-types/project/go.mod create mode 100644 test/projectgraph/cases/go/dependency-result-types/project/gomod/example.com/markup@v1.0.0/go.mod create mode 100644 test/projectgraph/cases/go/dependency-result-types/project/gomod/example.com/markup@v1.0.0/node.go create mode 100644 test/projectgraph/cases/go/dependency-result-types/project/gomod/example.com/markup@v1.0.0/token.go create mode 100644 test/projectgraph/cases/go/dependency-result-types/project/goroot/src/go.mod create mode 100644 test/projectgraph/cases/go/dependency-result-types/project/goroot/src/io/io.go create mode 100644 test/projectgraph/cases/go/dependency-result-types/project/goroot/src/net/http/request.go create mode 100644 test/projectgraph/cases/go/dependency-result-types/project/goroot/src/os/exec/exec.go create mode 100644 test/projectgraph/cases/go/dependency-result-types/project/goroot/src/os/file.go create mode 100644 test/projectgraph/cases/go/dependency-result-types/project/main.go create mode 100644 test/projectgraph/cases/go/dependency-result-types/summaries/go_example.com_markup_v1.0.0.json create mode 100644 test/projectgraph/cases/go/dependency-result-types/summaries/go_std_go1.25.1.json diff --git a/docs/projectgraph.md b/docs/projectgraph.md index 69097f2c..b5d9e068 100644 --- a/docs/projectgraph.md +++ b/docs/projectgraph.md @@ -163,13 +163,39 @@ of the highest confidence. A call that the link step cannot resolve is an unresolved site with a reason: `computed-name`, `dynamic-import`, `unknown-receiver`, -`no-definition`, `ambiguous` or `limit`. The site keeps the method name of -the call, so the reach query can tell which targets it concerns. +`no-definition`, `ambiguous`, `limit`, `interface-receiver` or +`no-dependency-source`. The site keeps the method name of the call, so the +reach query can tell which targets it concerns. A call on the result of another call, as `html.NewTokenizer(r).Next()`, needs the type of that result. When the link step does not know it, the -site is an `unknown-receiver` record with the method name, so a target of -that method is `uncertain` and not `no-such-target`. +site is a record with the method name, so a target of that method is +`uncertain` and not `no-such-target`. + +### Result types + +The Go index records the type name of each result of a function, by +position, and each name that an assignment or a short variable declaration +binds to a result, as `z` in `z := html.NewTokenizer(r)` and `err` in +`n, err := html.Parse(r)`. The blank name `_` binds nothing. The link step +reads the definition of the function, in the project or in the unit of +dependency code that gives its package, and binds the type of the result +to the name. A call on the name then gets the external key of the method +of the type, as `golang.org//x//net//html//Tokenizer//Next`, and a package +edge joins the key to the definition in the unit. A chained call, as +`pkg.F().M()` or `a.B().C()`, takes the type of each result in turn. + +| Result | What the call gets | +| --- | --- | +| A type of a unit, or of the project | The edge to the method of the type | +| An interface type | The `cha` edges to the implementations in the graph. An implementation in a unit gets its edge through its external key. With no implementation, the site is a record with the reason `interface-receiver` | +| A function of a package that no unit gives | A record with the reason `no-dependency-source` | +| A result with no named type, or a function that the unit does not define | A record with the reason `unknown-receiver` | + +The standard library is a unit too. Pass `GOROOT/src` as the unit +`go:std@`, as `-deps $(go env GOROOT)/src=go:std@go1.25.1`. It gives +each import path whose first element has no dot, as `net/http`. The index +step leaves out the `cmd` module of it, which no program imports. ## Overlay diff --git a/examples/projectgraph/main.go b/examples/projectgraph/main.go index 1738c22d..e27d3950 100644 --- a/examples/projectgraph/main.go +++ b/examples/projectgraph/main.go @@ -13,8 +13,9 @@ // -import-dir venv/site-packages -deps venv/site-packages/yaml=pypi:PyYAML@6.0.1, // keeps its path under the import directory, as yaml/__init__.py. Any // other -deps directory is an import directory of its own, and a pypi -// package given that way is its package. With -summary it prints the -// summary of each such unit as JSON. +// package given that way is its package. The Go standard library is a unit +// too, as -deps $(go env GOROOT)/src=go:std@go1.25.1. With -summary it +// prints the summary of each such unit as JSON. package main import ( diff --git a/fileindex/index.go b/fileindex/index.go index f7914868..5ed99c55 100644 --- a/fileindex/index.go +++ b/fileindex/index.go @@ -19,7 +19,7 @@ import ( // Version is the version of the index records and of the rules that fill // them. Bump it when a field, a kind, an order or a fill rule changes. A // store keys an index by Version, so the bump makes every older index miss. -const Version = "12" +const Version = "13" // DefaultTestPaths are the globs that mark the test files of the common // layouts, as projectgraph.Options.TestPaths takes them. A glob with no "/" @@ -88,6 +88,13 @@ const ( UnresolvedNoDefinition UnresolvedReason = "no-definition" UnresolvedAmbiguous UnresolvedReason = "ambiguous" UnresolvedLimit UnresolvedReason = "limit" + // UnresolvedInterfaceReceiver is a call on a value of an interface + // type that no type of the graph implements, so its code is elsewhere. + UnresolvedInterfaceReceiver UnresolvedReason = "interface-receiver" + // UnresolvedNoDependencySource is a call on a value whose type comes + // from a package with no code in the graph, as the result of a + // function of a module that no unit gives. + UnresolvedNoDependencySource UnresolvedReason = "no-dependency-source" ) type FlagKind string @@ -204,7 +211,9 @@ type Export struct { // // Arity, Variadic and Returns hold the signature of a Go function or // method: the count of its parameters, a last parameter of the form ...T, -// and the type name of its first result. Static marks a static method of +// and the type name of its first result. Results holds the type name of +// each result of a Go function with two or more results, by position, and +// "" for a result with no named type. Static marks a static method of // Java. type Definition struct { Key string `json:"key"` @@ -221,6 +230,7 @@ type Definition struct { Arity int `json:"arity,omitempty"` Variadic bool `json:"variadic,omitempty"` Returns string `json:"returns,omitempty"` + Results []string `json:"results,omitempty"` Static bool `json:"static,omitempty"` Flags []Flag `json:"flags,omitempty"` } @@ -250,12 +260,14 @@ type Signature struct { // Typed is a parameter, a field or a local variable with the type that it // declares, as parser in run(parser: Parser). Type is the type name as // written, with no type arguments and no pointer, as Parser or ns.Parser. -// Result marks a name that holds the first result of a call. Type then -// names the function, as NewParser in p := NewParser(). +// Result marks a name that holds a result of a call. Type then names the +// function, as NewParser in p := NewParser(), and Index is the position of +// the result, as 1 for err in p, err := NewParser(). type Typed struct { Name string `json:"name"` Type string `json:"type"` Result bool `json:"result,omitempty"` + Index int `json:"index,omitempty"` } // Call is one call site. Caller is the definition key that holds the site. @@ -600,6 +612,7 @@ func (ix *Index) EachString(fn func(string) string) { typed(def.Params) typed(def.Locals) kind(&def.Returns) + strs(def.Results) flags(def.Flags) } for i := range ix.Classes { diff --git a/fileindex/testdata/index.json b/fileindex/testdata/index.json index d5972d33..35ca47db 100644 --- a/fileindex/testdata/index.json +++ b/fileindex/testdata/index.json @@ -1,5 +1,5 @@ { - "version": "12", + "version": "13", "path": "src/app.py", "language": "python", "content_hash": "a172cedcae47474b615c54d510a5d84a8dea3032e958587430b413538be3f333", diff --git a/plugin/indexer/golang.go b/plugin/indexer/golang.go index c5d519ea..fcb30db1 100644 --- a/plugin/indexer/golang.go +++ b/plugin/indexer/golang.go @@ -184,7 +184,13 @@ func goSignature(p *projection, def *fileindex.Definition, node *sitter.Node) { var params []fileindex.Typed params, def.Arity, def.Variadic = p.goParams(node.ChildByFieldName("parameters"), p.goConstraints(node)) def.Params = append(def.Params, params...) - def.Returns = p.goResult(node.ChildByFieldName("result")) + results := p.goResults(node.ChildByFieldName("result")) + if len(results) > 0 { + def.Returns = results[0] + } + if len(results) > 1 { + def.Results = results + } def.Locals = p.goLocals(node.ChildByFieldName("body")) } @@ -253,20 +259,26 @@ func (p *projection) goParams(list *sitter.Node, constraints map[string]string) return typed, arity, variadic } -// goResult returns the type name of the first result of a function. -func (p *projection) goResult(result *sitter.Node) string { +// goResults returns the type name of each result of a function, by +// position. A result with no named type, as int or func(), gives "". +func (p *projection) goResults(result *sitter.Node) []string { if result == nil { - return "" + return nil } if result.Type() != "parameter_list" { - return p.goTypeName(result) + return []string{p.goTypeName(result)} } + var out []string for _, param := range ts.NamedChildren(result) { - if param.Type() == "parameter_declaration" { - return p.goTypeName(param.ChildByFieldName("type")) + if param.Type() != "parameter_declaration" { + continue + } + typ := p.goTypeName(param.ChildByFieldName("type")) + for range max(len(childrenByField(param, "name")), 1) { + out = append(out, typ) } } - return "" + return out } // goLocals returns the local variables of a function body whose type the @@ -297,21 +309,32 @@ func (p *projection) goLocals(body *sitter.Node) []fileindex.Typed { } // goAssigned returns the typed names of an assignment. One call on the -// right of more names gives its first result to the first name. +// right of more names gives each name the result at its position, and the +// blank name _ takes none. func (p *projection) goAssigned(names []*sitter.Node, right *sitter.Node) []fileindex.Typed { if right == nil { return nil } values := ts.NamedChildren(right) if len(values) == 1 && len(names) > 1 && values[0].Type() == "call_expression" { - names = names[:1] + t, ok := p.goValueType(values[0]) + if !ok || !t.Result { + return nil + } + var typed []fileindex.Typed + for i, name := range names { + if name.Type() == "identifier" && p.text(name) != "_" { + typed = append(typed, fileindex.Typed{Name: p.text(name), Type: t.Type, Result: true, Index: i}) + } + } + return typed } if len(values) != len(names) { return nil } var typed []fileindex.Typed for i, name := range names { - if name.Type() != "identifier" { + if name.Type() != "identifier" || p.text(name) == "_" { continue } if t, ok := p.goValueType(values[i]); ok { diff --git a/plugin/indexer/golang_test.go b/plugin/indexer/golang_test.go index 9927a568..0fcab12a 100644 --- a/plugin/indexer/golang_test.go +++ b/plugin/indexer/golang_test.go @@ -29,6 +29,7 @@ func TestFileIndexGo(t *testing.T) { " run(c.Add, k, f)", "}", "func Apply[T Saver, U any](t T, u U) { b := NewBox[int](1) }", + "func Pair() (_ *store.DB, ok, done bool) { _, x := New(\"y\"); return nil, x == nil, true }", }, "\n") + "\n" ix := indexSource(t, core.LanguageCodeGo, "shop.go", source) @@ -51,6 +52,7 @@ func TestFileIndexGo(t *testing.T) { assert.Equal(t, 2, def.Arity) assert.True(t, def.Variadic) assert.Equal(t, "Cart", def.Returns) + assert.Equal(t, []string{"Cart", ""}, def.Results, "a result of a predeclared type has no name") }) t.Run("a method has its receiver first and its typed locals", func(t *testing.T) { def := definitionOf(t, ix, "shop.go//Cart//Add") @@ -60,12 +62,19 @@ func TestFileIndexGo(t *testing.T) { assert.Equal(t, []fileindex.Typed{ {Name: "s", Type: "Saver"}, {Name: "d", Type: "New", Result: true}, + {Name: "err", Type: "New", Result: true, Index: 1}, {Name: "e", Type: "store.DB"}, {Name: "f", Type: "Cart"}, {Name: "g", Type: "Cart"}, {Name: "h", Type: "store.Open", Result: true}, }, def.Locals) }) + t.Run("each result has its position and the blank name binds none", func(t *testing.T) { + def := definitionOf(t, ix, "shop.go//Pair") + assert.Equal(t, "store.DB", def.Returns) + assert.Equal(t, []string{"store.DB", "", ""}, def.Results) + assert.Equal(t, []fileindex.Typed{{Name: "x", Type: "New", Result: true, Index: 1}}, def.Locals) + }) t.Run("a type parameter stands for its constraint", func(t *testing.T) { def := definitionOf(t, ix, "shop.go//Apply") assert.Equal(t, []fileindex.Typed{{Name: "t", Type: "Saver"}}, def.Params) diff --git a/plugin/indexer/gounit.go b/plugin/indexer/gounit.go index 0af0b5ab..421c8150 100644 --- a/plugin/indexer/gounit.go +++ b/plugin/indexer/gounit.go @@ -28,8 +28,34 @@ var ( goArch = []string{"386", "amd64", "arm", "arm64", "loong64", "mips", "mipsle", "mips64", "mips64le", "ppc64", "ppc64le", "riscv64", "s390x", "sparc64", "wasm"} ) +// goOutOfBuild reports whether the go command never builds a file of a Go +// dependency unit for an importer, by its path: a file that is not Go +// source, as the JavaScript of a web page in GOROOT/src, a test file, a +// file under a testdata directory or a directory whose name starts with _ +// or ., and a file of the cmd module of the unit of the standard library, +// as go:std@go1.25.1//cmd/go/main.go, which no program imports. Such a file +// holds no code that an importer runs, and its package clause can differ +// from the clause of its directory, as package main of a fixture. +func goOutOfBuild(file core.File) bool { + unit, inner := fileindex.SplitUnit(file.Name()) + pkg, err := fileindex.ParseUnit(unit) + if !file.IsImport() || err != nil || pkg.Ecosystem != "go" { + return false + } + if path.Ext(inner) != ".go" || strings.HasSuffix(inner, "_test.go") { + return true + } + dirs := strings.Split(path.Dir(inner), "/") + if pkg.Name == "std" && dirs[0] == "cmd" { + return true + } + return slices.ContainsFunc(dirs, func(d string) bool { + return d == "testdata" || strings.HasPrefix(d, "_") || strings.HasPrefix(d, ".") && d != "." + }) +} + // goExcluded reports whether no Go build reads a Go file of a dependency -// unit: a test file, a file with //go:build ignore, or a file whose +// unit by its build constraint: a file with //go:build ignore, or a file whose // constraint needs a tag that the go command never sets by itself, as // purego. Such a file holds no code that an importer runs by default. Its // package clause can differ from the clause of its directory, as package @@ -41,9 +67,6 @@ func goExcluded(file core.File, data []byte) bool { return false } name := path.Base(file.Name()) - if strings.HasSuffix(name, "_test.go") { - return true - } ctxt := build.Default ctxt.CgoEnabled = true ctxt.BuildTags, ctxt.ToolTags = nil, nil @@ -59,27 +82,34 @@ func goExcluded(file core.File, data []byte) bool { return true } -// generatedHeader matches the line that marks a generated Go file. -var generatedHeader = regexp.MustCompile(`(?m)^// Code generated .* DO NOT EDIT\.$`) +// generatedHeader matches the comment line that marks a generated Go file: +// the Code generated ... DO NOT EDIT. line of the Go convention, or an older +// line as generated by go run gen.go; DO NOT EDIT of x/net. +var generatedHeader = regexp.MustCompile(`(?m)^//.*\bDO NOT EDIT\b`) + +// packageClause matches the package clause of a Go file. The marker of a +// generated file comes before it. +var packageClause = regexp.MustCompile(`(?m)^package `) // isGeneratedTable reports whether a Go file of a dependency unit is a -// generated file or a table file, as idna/tables15.0.0.go of x/net. +// generated file or a table file, as idna/tables15.0.0.go or +// publicsuffix/table.go of x/net. func isGeneratedTable(name string, data []byte) bool { if unit, _ := fileindex.SplitUnit(name); unit == "" || path.Ext(name) != ".go" { return false } - if base := path.Base(name); strings.HasPrefix(base, "tables") { + if base := path.Base(name); strings.HasPrefix(base, "table") { return true } head := data - if i := bytes.Index(data, []byte("\npackage ")); i >= 0 { - head = data[:i] + if loc := packageClause.FindIndex(data); loc != nil { + head = data[:loc[0]] } return generatedHeader.Match(head) } // goSkeleton returns the bytes of a Go file with each value of a -// top-level var that holds only data replaced by 0 and spaces. The +// top-level var or const that holds only data replaced by 0 and spaces. The // declarations keep their lines and columns, and the file still parses. The // data values of a generated table file are what hit the limits of the // index, as a string of thousands of concatenated parts. Function bodies and @@ -99,7 +129,7 @@ func goSkeleton(tree *sitter.Tree, data []byte) []byte { switch node.Type() { case "function_declaration", "method_declaration": return - case "var_spec": + case "var_spec", "const_spec": if values := node.ChildByFieldName("value"); values != nil { for _, v := range ts.NamedChildren(values) { if !holdsCode(v) { diff --git a/plugin/indexer/gounit_test.go b/plugin/indexer/gounit_test.go index 02432a8e..24551c7e 100644 --- a/plugin/indexer/gounit_test.go +++ b/plugin/indexer/gounit_test.go @@ -97,3 +97,21 @@ func TestGoSkeletonKeepsTheHashOfTheFile(t *testing.T) { require.NoError(t, err) assert.Equal(t, fileindex.FileCached, result.Dependencies[goKitUnit+"//tables.go"], "the store serves the skeleton index on the next run") } + +func TestIsGeneratedTable(t *testing.T) { + for _, tt := range []struct { + name string + data string + want bool + }{ + {name: goKitUnit + "//gen/out.go", data: "// Code generated by synth. DO NOT EDIT.\n\npackage gen\n", want: true}, + {name: goKitUnit + "//publicsuffix/list.go", data: "// generated by go run gen.go; DO NOT EDIT\n\npackage publicsuffix\n", want: true}, + {name: goKitUnit + "//idna/tables15.0.0.go", data: "package idna\n", want: true}, + {name: goKitUnit + "//kit.go", data: "package kit\n\n// DO NOT EDIT after the package clause.\n", want: false}, + {name: "tables.go", data: "// Code generated by synth. DO NOT EDIT.\n\npackage main\n", want: false}, + } { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.want, isGeneratedTable(tt.name, []byte(tt.data))) + }) + } +} diff --git a/plugin/indexer/indexer.go b/plugin/indexer/indexer.go index 58fdcccd..2c8e4a7e 100644 --- a/plugin/indexer/indexer.go +++ b/plugin/indexer/indexer.go @@ -269,14 +269,17 @@ func (r *run) index(ctx context.Context, files []core.File, byExtension map[stri r.result.Report.Skipped = append(r.result.Report.Skipped, file.Name()) continue } + if goOutOfBuild(file) { + r.exclude(file) + continue + } data, err := read(file, r.opts.MaxFileBytes) if err != nil { r.fail(ctx, file, core.StageRead, err) continue } if goExcluded(file, data) { - r.excluded[file.Name()] = true - r.result.Excluded = append(r.result.Excluded, file.Name()) + r.exclude(file) continue } @@ -462,6 +465,13 @@ func (r *run) done(file core.File, ix *fileindex.Index, status fileindex.FileSta r.result.Indexes = append(r.result.Indexes, ix) } +// exclude records a file of dependency code that the build of the platform +// does not read. +func (r *run) exclude(file core.File) { + r.excluded[file.Name()] = true + r.result.Excluded = append(r.result.Excluded, file.Name()) +} + func (r *run) fail(ctx context.Context, file core.File, stage core.Stage, err error) { r.result.Report.Failed = append(r.result.Report.Failed, plugin.FileError{File: file.Name(), Stage: stage, Err: err}) r.result.statuses(file.Name())[file.Name()] = status(ctx) diff --git a/plugin/indexer/plugin_test.go b/plugin/indexer/plugin_test.go index 4aa19c88..b2959272 100644 --- a/plugin/indexer/plugin_test.go +++ b/plugin/indexer/plugin_test.go @@ -101,6 +101,7 @@ var goldenHashes = map[string]string{ "10": "6053fe7c2b8fef2888806debc47372948eec366beb800419c1e435f021711fa0", "11": "141e6d6a6f6d369635bf4f6781033acc269e59fe5f722558192b8e952c7e7bd9", "12": "974943df4185f9c3070dcfdfedff8e91912e257d255a936f7c27608bb63a69f0", + "13": "97614d8b96782e65813ee409e4a3f33c4655157b5dfa58baf17cd3677958c619", } func TestFileIndexGoldenHashFollowsVersion(t *testing.T) { diff --git a/plugin/indexer/testdata/golden/App.cs.json b/plugin/indexer/testdata/golden/App.cs.json index 9d30a79e..9bcd0d82 100644 --- a/plugin/indexer/testdata/golden/App.cs.json +++ b/plugin/indexer/testdata/golden/App.cs.json @@ -1,5 +1,5 @@ { - "version": "12", + "version": "13", "path": "App.cs", "language": "csharp", "content_hash": "8991919e54610418b58a53fb6b26dc563c5ef5af684165b60e8af5b6e7e8773c", diff --git a/plugin/indexer/testdata/golden/App.java.json b/plugin/indexer/testdata/golden/App.java.json index c0de344a..057a6be8 100644 --- a/plugin/indexer/testdata/golden/App.java.json +++ b/plugin/indexer/testdata/golden/App.java.json @@ -1,5 +1,5 @@ { - "version": "12", + "version": "13", "path": "App.java", "language": "java", "content_hash": "4b93f5c2c4ed8e1881cb857a9b7cd65c3a0d4cb4cd0929140539d0a418a27a95", diff --git a/plugin/indexer/testdata/golden/App.kt.json b/plugin/indexer/testdata/golden/App.kt.json index be6dbb5a..a9e81596 100644 --- a/plugin/indexer/testdata/golden/App.kt.json +++ b/plugin/indexer/testdata/golden/App.kt.json @@ -1,5 +1,5 @@ { - "version": "12", + "version": "13", "path": "App.kt", "language": "kotlin", "content_hash": "458147a6c6ebc2561187207bb3bca7e091914bfd2e52f0540f58887976983462", diff --git a/plugin/indexer/testdata/golden/app.go.json b/plugin/indexer/testdata/golden/app.go.json index bc978c06..0c27098c 100644 --- a/plugin/indexer/testdata/golden/app.go.json +++ b/plugin/indexer/testdata/golden/app.go.json @@ -1,5 +1,5 @@ { - "version": "12", + "version": "13", "path": "app.go", "language": "go", "content_hash": "3d65f36ed0edfd5e009c06d07d3d038337e9faebaca6c9762b5c473a14826166", @@ -86,7 +86,11 @@ "exported": true, "arity": 2, "variadic": true, - "returns": "Server" + "returns": "Server", + "results": [ + "Server", + "" + ] }, { "key": "app.go//Server//Run", @@ -125,6 +129,12 @@ "type": "NewServer", "result": true }, + { + "name": "err", + "type": "NewServer", + "result": true, + "index": 1 + }, { "name": "e", "type": "store.DB" diff --git a/plugin/indexer/testdata/golden/app.js.json b/plugin/indexer/testdata/golden/app.js.json index 3f4d28dc..27e177dc 100644 --- a/plugin/indexer/testdata/golden/app.js.json +++ b/plugin/indexer/testdata/golden/app.js.json @@ -1,5 +1,5 @@ { - "version": "12", + "version": "13", "path": "app.js", "language": "javascript", "content_hash": "247fdd512217fb5e7ce19cbf3f60b327e145abc15a26ee510e167cbc651b0a54", diff --git a/plugin/indexer/testdata/golden/app.php.json b/plugin/indexer/testdata/golden/app.php.json index 24b1237b..093123ee 100644 --- a/plugin/indexer/testdata/golden/app.php.json +++ b/plugin/indexer/testdata/golden/app.php.json @@ -1,5 +1,5 @@ { - "version": "12", + "version": "13", "path": "app.php", "language": "php", "content_hash": "b57d8d52aafac8d7cbe2d19e5889e4e7b851ee71a48ea966cdd9cbfd14214466", diff --git a/plugin/indexer/testdata/golden/app.py.json b/plugin/indexer/testdata/golden/app.py.json index 1245b895..9a29ae43 100644 --- a/plugin/indexer/testdata/golden/app.py.json +++ b/plugin/indexer/testdata/golden/app.py.json @@ -1,5 +1,5 @@ { - "version": "12", + "version": "13", "path": "app.py", "language": "python", "content_hash": "a9cdb5fb012d2a29970c07deda9cc8360983015e2360cb9091bca7bfce5c801d", diff --git a/plugin/indexer/testdata/golden/app.rb.json b/plugin/indexer/testdata/golden/app.rb.json index d84f0b64..5b97f003 100644 --- a/plugin/indexer/testdata/golden/app.rb.json +++ b/plugin/indexer/testdata/golden/app.rb.json @@ -1,5 +1,5 @@ { - "version": "12", + "version": "13", "path": "app.rb", "language": "ruby", "content_hash": "f3a3e601660e0130750119df4267cac37972c412ccc8f5d8f16e3ba516f659d9", diff --git a/plugin/indexer/testdata/golden/app.rs.json b/plugin/indexer/testdata/golden/app.rs.json index fcf3c271..fbd807eb 100644 --- a/plugin/indexer/testdata/golden/app.rs.json +++ b/plugin/indexer/testdata/golden/app.rs.json @@ -1,5 +1,5 @@ { - "version": "12", + "version": "13", "path": "app.rs", "language": "rust", "content_hash": "f07a751f9610f47e7b49679223d4ca80f85ecb21abde61488935d2f21a5c3603", diff --git a/plugin/indexer/testdata/golden/app.ts.json b/plugin/indexer/testdata/golden/app.ts.json index 3e1d08ed..383c7bc5 100644 --- a/plugin/indexer/testdata/golden/app.ts.json +++ b/plugin/indexer/testdata/golden/app.ts.json @@ -1,5 +1,5 @@ { - "version": "12", + "version": "13", "path": "app.ts", "language": "typescript", "content_hash": "d3908948ab8ec7b3adc898e0707ce74bb3a2d6393a724dfbeb67147840397234", diff --git a/plugin/indexer/testdata/golden/exports.js.json b/plugin/indexer/testdata/golden/exports.js.json index 85da7d88..6475eb62 100644 --- a/plugin/indexer/testdata/golden/exports.js.json +++ b/plugin/indexer/testdata/golden/exports.js.json @@ -1,5 +1,5 @@ { - "version": "12", + "version": "13", "path": "exports.js", "language": "javascript", "content_hash": "765716b5977b05d7bbc4ef5e0192e196e0209400366036053df632d54fdb0f95", diff --git a/plugin/indexer/testdata/golden/exports.py.json b/plugin/indexer/testdata/golden/exports.py.json index 307cfa11..15577c3f 100644 --- a/plugin/indexer/testdata/golden/exports.py.json +++ b/plugin/indexer/testdata/golden/exports.py.json @@ -1,5 +1,5 @@ { - "version": "12", + "version": "13", "path": "exports.py", "language": "python", "content_hash": "653115ddc22536763049e9209d4942bca5c6817b98bc01722879b69c04d8605b", diff --git a/plugin/indexer/testdata/golden/exports.ts.json b/plugin/indexer/testdata/golden/exports.ts.json index f22cd269..81963b85 100644 --- a/plugin/indexer/testdata/golden/exports.ts.json +++ b/plugin/indexer/testdata/golden/exports.ts.json @@ -1,5 +1,5 @@ { - "version": "12", + "version": "13", "path": "exports.ts", "language": "typescript", "content_hash": "0edcb9324080b924f3ac45d57f5b5cbe90d625038a0dd7f99fa9e5fcbeee1edc", diff --git a/plugin/indexer/testdata/golden/flags.js.json b/plugin/indexer/testdata/golden/flags.js.json index 86bb9b56..ebaeaa2f 100644 --- a/plugin/indexer/testdata/golden/flags.js.json +++ b/plugin/indexer/testdata/golden/flags.js.json @@ -1,5 +1,5 @@ { - "version": "12", + "version": "13", "path": "flags.js", "language": "javascript", "content_hash": "70731d0448bc5af9ecf5c81156eefbd39535a3fc674fffc250937d76c2ba4610", diff --git a/plugin/indexer/testdata/golden/flags.py.json b/plugin/indexer/testdata/golden/flags.py.json index bcd5396d..34f3d01c 100644 --- a/plugin/indexer/testdata/golden/flags.py.json +++ b/plugin/indexer/testdata/golden/flags.py.json @@ -1,5 +1,5 @@ { - "version": "12", + "version": "13", "path": "flags.py", "language": "python", "content_hash": "d3169e0c3c00508c3f9492866cf4db99ddd780d42dc9e4438558d1d4a22f2621", diff --git a/plugin/indexer/testdata/golden/flags.ts.json b/plugin/indexer/testdata/golden/flags.ts.json index 04b60ca3..a2017009 100644 --- a/plugin/indexer/testdata/golden/flags.ts.json +++ b/plugin/indexer/testdata/golden/flags.ts.json @@ -1,5 +1,5 @@ { - "version": "12", + "version": "13", "path": "flags.ts", "language": "typescript", "content_hash": "a84c4ad21735c1c250e33317463237749489ea1f220a93f97e2bea3f07b040c4", diff --git a/projectgraph/declared.go b/projectgraph/declared.go index c701c3c8..abe06224 100644 --- a/projectgraph/declared.go +++ b/projectgraph/declared.go @@ -10,9 +10,10 @@ import ( // typedName returns the class or interface that the declared type of a // parameter or a typed local variable of the name names. The name belongs // to the scope or to a function that holds the scope, as the parameter of a -// method in an arrow function of the method. A Go local that holds the -// result of a call has the type of the first result of the function. -func (b *builder) typedName(sc *scope, scopeKey, name string) (string, bool) { +// method in an arrow function of the method. A Go local that holds a +// result of a call has the type of the result at its position. A name +// with no known type gives the reason of the record of a call on it. +func (b *builder) typedName(sc *scope, scopeKey, name string) (string, fileindex.UnresolvedReason, bool) { for key := scopeKey; fileindex.HasKeyPrefix(key, sc.ix.Path) && key != sc.ix.Path; key = fileindex.Parent(key) { def := b.defs[key] if def == nil { @@ -24,42 +25,35 @@ func (b *builder) typedName(sc *scope, scopeKey, name string) (string, bool) { continue } if !t.Result { - return b.resolveType(sc, key, t.Type) + class, ok := b.resolveType(sc, key, t.Type) + return class, fileindex.UnresolvedUnknownReceiver, ok } fn, ok := b.resolveScopeName(sc, t.Type) if !ok { - return "", false + return "", fileindex.UnresolvedUnknownReceiver, false } - return b.resultType(fn) + return b.resultType(fileindex.PackageOf(sc.ix.Path), fn, t.Index) } } } - return "", false + return "", fileindex.UnresolvedUnknownReceiver, false } // memberType returns the class of a name in a scope: a typed parameter or // local variable, or in Java a field of a class that holds the scope, which -// a method names with no this. -func (b *builder) memberType(sc *scope, scopeKey, name string) (string, bool) { - if class, ok := b.typedName(sc, scopeKey, name); ok || !sc.profile.implicitFields { - return class, ok +// a method names with no this. A name with no known type gives the reason +// of the record of a call on it. +func (b *builder) memberType(sc *scope, scopeKey, name string) (string, fileindex.UnresolvedReason, bool) { + class, reason, ok := b.typedName(sc, scopeKey, name) + if ok || !sc.profile.implicitFields { + return class, reason, ok } for class, ok := b.enclosingClass(scopeKey); ok; class, ok = b.enclosingClass(fileindex.Parent(class)) { if t, ok := b.fieldType(class, name); ok { - return t, true + return t, "", true } } - return "", false -} - -// resultType returns the class or interface that the first result of a Go -// function names. -func (b *builder) resultType(fn string) (string, bool) { - def := b.defs[fn] - if def == nil || def.Returns == "" { - return "", false - } - return b.resolveType(b.scopes[fileindex.Unit(fn)], fn, def.Returns) + return "", reason, false } // fieldType returns the class or interface that the declared type of a diff --git a/projectgraph/golang.go b/projectgraph/golang.go index f1e390e8..1486b1c5 100644 --- a/projectgraph/golang.go +++ b/projectgraph/golang.go @@ -22,6 +22,7 @@ var goProfile = profile{ provide: goProvide, narrowProviders: goNarrowProviders, surface: goSurface, + symbol: goSymbol, dependencies: func(b *builder) ([]string, []modules.Problem) { return b.project.golang.Dependencies(), nil }, implicitInterfaces: true, typeConversions: true, @@ -30,10 +31,21 @@ var goProfile = profile{ } // goProvide returns the package unit of a Go module that an import path -// under its module path names. +// under its module path names. The unit of the standard library of +// GOROOT/src, as the unit go:std@go1.25.1, gives each import path whose +// first element has no dot. func goProvide(_ *builder, sp *space, module string, _ modules.Form) []provision { + if sp.pkg.Ecosystem != "go" { + return nil + } rel, ok := strings.CutPrefix(module, sp.pkg.Name) - if sp.pkg.Ecosystem != "go" || !ok || rel != "" && !strings.HasPrefix(rel, "/") { + switch { + case sp.pkg.Name == modules.StdModule: + if !modules.IsStdPath(module) { + return nil + } + rel = module + case !ok || rel != "" && !strings.HasPrefix(rel, "/"): return nil } dir, ok := sp.golang.Resolve(module) @@ -82,6 +94,18 @@ func goSurface(s *surfacer) { } } +// goSymbol returns the import path of the package of a Go definition of a +// unit and the names of the definition, as golang.org/x/net/html and +// Tokenizer, Next. +func goSymbol(b *builder, sp *space, def string) (string, []string, bool) { + dir := modules.Dir(sp.inner(fileindex.Unit(def))) + if sp.goPackages[dir] == nil { + return "", nil, false + } + importPath, ok := b.goImportPath(sp, dir) + return importPath, fileindex.Segments(def), ok +} + // goImportPath returns the import path of a Go package of a unit. A package // under an internal directory and a main package have no importer outside // the module. diff --git a/projectgraph/graph.go b/projectgraph/graph.go index 6de75899..a08ad26a 100644 --- a/projectgraph/graph.go +++ b/projectgraph/graph.go @@ -17,7 +17,7 @@ import ( // Version is the version of the link rules. Bump it when a rule that makes a // node, an edge, a root or an unresolved site changes, so a diff of two // reports shows the change. -const Version = "12" +const Version = "13" type NodeKind string diff --git a/projectgraph/link.go b/projectgraph/link.go index 9890f31d..032ea612 100644 --- a/projectgraph/link.go +++ b/projectgraph/link.go @@ -123,8 +123,10 @@ type builder struct { // defs holds the definition records by key. defs map[string]*fileindex.Definition // reading holds the variables whose value a walk reads now, so a cycle - // of variables across files stops. - reading map[string]bool + // of variables across files stops. stepping holds the names that a + // walk of a unit reads now for the result type of an external key. + reading map[string]bool + stepping map[nameKey]bool // groupMembers holds the members of each group of aliases by its first // alias, and nodesByParent the keys of the nodes by their parent. @@ -261,6 +263,7 @@ func newTables(indexes []*fileindex.Index, opts Options) *builder { classes: map[string]classRecord{}, defs: make(map[string]*fileindex.Definition, defs), reading: map[string]bool{}, + stepping: map[nameKey]bool{}, findingBases: map[string]bool{}, state: map[langcode.Code]any{}, units: map[string]*unit{}, diff --git a/projectgraph/modules/golang.go b/projectgraph/modules/golang.go index ae6a3edc..42d2db94 100644 --- a/projectgraph/modules/golang.go +++ b/projectgraph/modules/golang.go @@ -12,6 +12,18 @@ import ( var errNoModule = errors.New("go.mod has no module directive") +// StdModule is the module path of the standard library in GOROOT/src. The +// import path of each of its packages is the directory, as net/http, with +// no module prefix. +const StdModule = "std" + +// IsStdPath reports whether an import path can name a package of the +// standard library: its first element has no dot. +func IsStdPath(spec string) bool { + first, _, _ := strings.Cut(spec, "/") + return first != "" && !strings.Contains(first, ".") +} + // goIgnoredDirs hold Go code that is not a package of the module. vendor // holds the code of a dependency, which stays external, and the go command // skips testdata. @@ -94,7 +106,9 @@ func (g *Golang) Resolve(spec string) (string, bool) { // directory of the project can name it. func (g *Golang) FirstParty(spec string) bool { if len(g.modules) > 0 { - return slices.ContainsFunc(g.modules, func(m goModule) bool { return underPath(spec, m.path) }) + return slices.ContainsFunc(g.modules, func(m goModule) bool { + return underPath(spec, m.path) || m.path == StdModule && IsStdPath(spec) + }) } // Each suffix of the path after a slash is a lookup, because a scan of // the directories at each import grows with the square of the project. @@ -121,6 +135,9 @@ func (g *Golang) ImportPath(dir string) (string, bool) { if slices.ContainsFunc(strings.Split(rel, "/"), func(part string) bool { return slices.Contains(goIgnoredDirs, part) }) { return "", false } + if m.path == StdModule { + return rel, rel != "" + } return path.Join(m.path, rel), true } return "", false diff --git a/projectgraph/modules/golang_test.go b/projectgraph/modules/golang_test.go index f9578264..bcbfe080 100644 --- a/projectgraph/modules/golang_test.go +++ b/projectgraph/modules/golang_test.go @@ -67,3 +67,23 @@ func TestGolangWithNoModule(t *testing.T) { assert.False(t, g.FirstParty("fmt"), "the root directory names no import path") assert.Empty(t, g.Problems()) } + +func TestGolangStandardLibrary(t *testing.T) { + paths := []string{"net/http/client.go", "internal/poll/fd.go", "cmd/go/main.go", "vendor/golang.org/x/net/http2/h2.go"} + root := fstest.MapFS{ + "go.mod": {Data: []byte("module std\n\ngo 1.25\n")}, + "cmd/go.mod": {Data: []byte("module cmd\n\ngo 1.25\n")}, + } + g := NewGolang(NewFiles(paths), root, []string{"net/http", "internal/poll", "cmd/go", "vendor/golang.org/x/net/http2"}) + + dir, ok := g.Resolve("net/http") + assert.True(t, ok) + assert.Equal(t, "net/http", dir, "a package of std has its directory as its import path") + dir, ok = g.Resolve("cmd/go") + assert.True(t, ok) + assert.Equal(t, "cmd/go", dir) + _, ok = g.Resolve("golang.org/x/net/http2") + assert.False(t, ok, "vendor stays external") + assert.True(t, g.FirstParty("net/url")) + assert.False(t, g.FirstParty("golang.org/x/net/html")) +} diff --git a/projectgraph/profile.go b/projectgraph/profile.go index 4eadb95f..c04fbb2c 100644 --- a/projectgraph/profile.go +++ b/projectgraph/profile.go @@ -47,6 +47,11 @@ type profile struct { // surface adds the exported symbols of a unit of dependency code with // the import that names each one. The profile of the family holds it. surface func(s *surfacer) + // symbol returns the module that an importer of a definition of a unit + // of dependency code imports, and the names of the definition under + // it, as golang.org/x/net/html and Tokenizer. The profile of the family + // holds it. + symbol func(b *builder, sp *space, def string) (string, []string, bool) // dependencies returns the packages that the manifests of the project // declare for the ecosystem of the family, and the manifests that the // reader could not read. The profile of the family holds it. diff --git a/projectgraph/resolve.go b/projectgraph/resolve.go index b71015d5..cb83e2c7 100644 --- a/projectgraph/resolve.go +++ b/projectgraph/resolve.go @@ -90,7 +90,7 @@ func (b *builder) linkCalls(ix *fileindex.Index) { if r.unbound && sc.profile.thisCalls { r = b.thisCall(ix, caller, call, r) } - b.linkCHA(call, r, from, site) + overrides := b.linkCHA(call, r, from, site) // A declared type gives the class of the receiver, so the // link step owns the record of the site and no name edge // goes from it. @@ -98,13 +98,19 @@ func (b *builder) linkCalls(ix *fileindex.Index) { b.narrow(site, call.Callee) } if r.fail != nil { + reason := r.fail.reason + // A Go value of an interface type that no type of the + // graph implements holds a type of code elsewhere. + if reason == fileindex.UnresolvedNoDefinition && overrides == 0 && r.declared && sc.profile.implicitInterfaces && b.kindOf(r.class) == NodeInterface { + reason = fileindex.UnresolvedInterfaceReceiver + } b.addUnresolved(UnresolvedSite{ Caller: from, Site: site, Text: call.Callee, Receiver: call.Receiver, Method: cmp.Or(call.Method, r.fail.name), - Reason: r.fail.reason, + Reason: reason, }) continue } @@ -114,8 +120,10 @@ func (b *builder) linkCalls(ix *fileindex.Index) { if r.unbound && !b.unboundEdge(ix, call, from, site, r, unknown[[2]int{call.Line, call.Column}]) { continue } - if b.onCallResult(sc.profile, call, r) { - b.addUnresolved(UnresolvedSite{Caller: from, Site: site, Text: call.Callee, Receiver: call.Receiver, Method: call.Method, Reason: fileindex.UnresolvedUnknownReceiver}) + // The index of some languages records such a call itself, and + // that record stays the one record of the site. + if reason, ok := b.onCallResult(sc.profile, call, r); ok && !unknown[[2]int{call.Line, call.Column}] { + b.addUnresolved(UnresolvedSite{Caller: from, Site: site, Text: call.Callee, Receiver: call.Receiver, Method: call.Method, Reason: reason}) } switch { case r.kind == EdgeDirect && isAssignment(sc, call, r.target): @@ -131,27 +139,35 @@ func (b *builder) linkCalls(ix *fileindex.Index) { } } -// onCallResult reports whether a resolution took the method of a call on -// the result of another call as a member of the external key of the called -// name, as html//NewTokenizer//Next for html.NewTokenizer(r).Next(). The -// link step does not know the type of that result, so the key can name no -// symbol, and a target of the method of the real type would read as -// no-such-target. The edge stays, because the called name can be a type, -// as in a Go conversion http.HandlerFunc(f).ServeHTTP(w, r). +// onCallResult returns the reason of the record of a call whose resolution +// took its method as a member of the external key of the called name of a +// call on a call result, as html//NewTokenizer//Next for +// html.NewTokenizer(r).Next(). The link step does not know the type of +// that result, so the key can name no symbol, and a target of the method +// of the real type would read as no-such-target. The edge stays, because +// the called name can be a type, as in a Go conversion +// http.HandlerFunc(f).ServeHTTP(w, r). The reason is no-dependency-source +// when no unit of dependency code gives the package of the key. // // A construction gives an instance of the called class, so the key is // right: new Yaml().load() in Java, or Yaml().load() in Python or Kotlin, // whose classes start with an upper case letter. A Go name starts with an // upper case letter when the package exports it, so it tells nothing. -func (b *builder) onCallResult(p *profile, call fileindex.Call, r resolution) bool { +func (b *builder) onCallResult(p *profile, call fileindex.Call, r resolution) (fileindex.UnresolvedReason, bool) { called, ok := calledName(call.Receiver) if !ok || r.fail != nil || call.Method == "" || b.kindOf(r.key) != NodeExternal { - return false + return "", false } if strings.HasPrefix(call.Receiver, "new ") || !p.typeConversions && startsUpper(called) { - return false + return "", false + } + if fileindex.Last(r.key) != call.Method || fileindex.Last(fileindex.Parent(r.key)) != called { + return "", false + } + if _, provided := b.unitDefinitions(b.origins[r.key]); !provided { + return fileindex.UnresolvedNoDependencySource, true } - return fileindex.Last(r.key) == call.Method && fileindex.Last(fileindex.Parent(r.key)) == called + return fileindex.UnresolvedUnknownReceiver, true } // calledName returns the name of the call that ends a receiver as written, @@ -254,8 +270,10 @@ func (b *builder) unboundEdge(ix *fileindex.Index, call fileindex.Call, from str // step. // // A receiver that a call of the class makes, as p = Parser(), holds an -// instance of that class only, so it gets no cha edge. -func (b *builder) linkCHA(call fileindex.Call, r resolution, from string, site Site) { +// instance of that class only, so it gets no cha edge. An override in a +// unit of dependency code gets its edge through its external key. It +// returns the count of the overrides. +func (b *builder) linkCHA(call fileindex.Call, r resolution, from string, site Site) int { lang := b.byPath[site.Path].Language class, virtual := r.class, r.virtual // A Java call with no receiver runs on this. @@ -276,11 +294,11 @@ func (b *builder) linkCHA(call fileindex.Call, r resolution, from string, site S } switch { case !virtual || class == "" || call.Method == "" || !b.dispatches(lang, class): - return + return 0 case r.fail != nil && r.fail.name != call.Method: - return + return 0 case r.fail == nil && fileindex.Last(r.key) != call.Method: - return + return 0 } b.narrow(site, call.Callee) found := "" @@ -289,7 +307,7 @@ func (b *builder) linkCHA(call fileindex.Call, r resolution, from string, site S } overrides := b.overrides(class, call, found) for _, key := range overrides { - b.link(lang, from, key, site, EdgeCHA, ruleCHA) + b.link(lang, from, b.entryKey(from, key), site, EdgeCHA, ruleCHA) } // An ECMAScript type is structural. A value of an interface type can be // an object literal or an instance of a class with no implements, and an @@ -298,6 +316,7 @@ func (b *builder) linkCHA(call fileindex.Call, r resolution, from string, site S if profileOf(lang).structuralTypes && (b.kindOf(class) == NodeInterface || r.fail != nil && len(overrides) == 0) { b.structural[siteCall{site, call.Callee}] = true } + return len(overrides) } // linkReferences adds a reference edge from the definition that holds each @@ -359,7 +378,7 @@ func (b *builder) resolveTarget(sc *scope, caller, target string) []resolution { return []resolution{b.resolveLocal(sc, caller, target)} } if name, rest, ok := strings.Cut(target, fileindex.Separator); ok { - if class, ok := b.memberType(sc, caller, name); ok { + if class, _, ok := b.memberType(sc, caller, name); ok { return []resolution{b.walkTyped(class, strings.Split(rest, fileindex.Separator))} } } @@ -368,7 +387,7 @@ func (b *builder) resolveTarget(sc *scope, caller, target string) []resolution { names := append(lead, rest...) mods := b.resolveModules(ix, module, formOf(imp)) if len(mods) == 0 { - return []resolution{{key: b.importedExternal(ix, target, module, formOf(imp), names), kind: EdgeDirect, rule: ruleExternal}} + return []resolution{b.importedWalk(ix, target, module, formOf(imp), names)} } if len(names) == 0 && sc.profile.defaultExport != "" { names = []string{sc.profile.defaultExport} @@ -526,8 +545,9 @@ func (b *builder) resolveLocal(sc *scope, caller, target string) resolution { } r := b.walk(prefix, segs[i:]) if fileindex.HasKeyPrefix(caller, prefix) && b.isFunction(prefix) { - r = resolution{fail: &failure{reason: fileindex.UnresolvedUnknownReceiver, name: segs[i]}} - if class, ok := b.memberType(sc, prefix, segs[i]); ok { + class, reason, ok := b.memberType(sc, prefix, segs[i]) + r = resolution{fail: &failure{reason: reason, name: segs[i]}} + if ok { r = b.walkTyped(class, segs[i+1:]) } } diff --git a/projectgraph/results.go b/projectgraph/results.go new file mode 100644 index 00000000..d201fec2 --- /dev/null +++ b/projectgraph/results.go @@ -0,0 +1,184 @@ +package projectgraph + +import ( + "slices" + "strings" + + "github.com/safedep/code/fileindex" + "github.com/safedep/code/projectgraph/modules" +) + +// resultType returns the class or interface of the result at a position of +// a function, as a file of the unit of the importer sees it, "" for the +// project. A function of a package +// that the project imports is an external key, and the unit of dependency +// code that gives the package gives its definition. A function of a +// package that no unit gives has no known result, so a call on the result +// is uncertain with the reason no-dependency-source. +func (b *builder) resultType(unit, fn string, pos int) (string, fileindex.UnresolvedReason, bool) { + defs := []string{fn} + if b.defs[fn] == nil && b.g.IsExternal(fn) { + var provided bool + defs, provided = b.unitDefinitions(b.origins[fn]) + if !provided { + return "", fileindex.UnresolvedNoDependencySource, false + } + } + for _, d := range defs { + text := resultText(b.defs[d], pos) + if text == "" { + continue + } + if class, ok := b.resolveType(b.scopes[fileindex.Unit(d)], d, text); ok { + return b.typeFor(unit, class), "", true + } + } + return "", fileindex.UnresolvedUnknownReceiver, false +} + +// resultText returns the type name of the result at a position of a +// function, as written. +func resultText(def *fileindex.Definition, pos int) string { + switch { + case def == nil: + return "" + case pos < len(def.Results): + return def.Results[pos] + case pos == 0: + return def.Returns + } + return "" +} + +// unitDefinitions returns the definitions that the units of dependency +// code give for the origins of an external key, and whether a unit gives +// the package of an origin at all. A walk of a unit can ask for the +// definitions of another external key, so a key that a walk reads now +// gives none. +func (b *builder) unitDefinitions(origins []origin) ([]string, bool) { + var defs []string + provided := false + for _, o := range origins { + for _, pv := range b.providers(o) { + provided = true + if pv.module == "" || len(o.names) == 0 { + continue + } + k := nameKey{module: pv.module, name: fileindex.Join("", o.names...)} + if b.stepping[k] { + continue + } + b.stepping[k] = true + r := b.walk(pv.module, o.names) + delete(b.stepping, k) + if r.fail == nil && b.defs[r.key] != nil && !slices.Contains(defs, r.key) { + defs = append(defs, r.key) + } + } + } + return defs, provided +} + +// externalResult returns the type of the result of the function or the +// method of the name under an external key, as golang.org//x//net//html//Tokenizer +// for NewTokenizer under golang.org//x//net//html. A unit of dependency +// code gives the function, and the importer of the key sees the type. +func (b *builder) externalResult(parent, name string) (string, bool) { + origins := derivedOrigins(b.origins[parent], name) + if len(origins) == 0 { + return "", false + } + defs, _ := b.unitDefinitions(origins) + for _, d := range defs { + if !b.isFunction(d) { + continue + } + if class, _, ok := b.resultType(origins[0].unit, d, 0); ok { + return class, true + } + } + return "", false +} + +// derivedOrigins returns the origins of the name under a key with the +// origins. +func derivedOrigins(origins []origin, names ...string) []origin { + out := make([]origin, 0, len(origins)) + for _, o := range origins { + o.names = append(slices.Clone(o.names), names...) + out = append(out, o) + } + return out +} + +// typeFor returns the key of a class as a file of a unit names it, "" for +// the project. +// A class of a unit of dependency code that the importer does not belong +// to is its external key, as golang.org//x//net//html//Tokenizer, so a call +// of its method gets the external key that a target names, and a package +// edge then joins the key to the definition. An interface stays as it is, +// because the class hierarchy analysis reads its implementations. +func (b *builder) typeFor(unit, class string) string { + if fileindex.PackageOf(class) == unit || b.kindOf(class) == NodeInterface || !b.isType(class) { + return class + } + if key, ok := b.symbolKey(unit, class); ok { + return key + } + return class +} + +// entryKey returns the key of a definition that a caller runs: the +// definition itself, or for a definition of a unit of dependency code that +// the caller does not belong to, its external key with a node. The +// package edge of the key then joins it to the definition, and a target +// of the key finds the path. +func (b *builder) entryKey(from, def string) string { + if sameUnit(from, def) { + return def + } + if key, ok := b.symbolKey(fileindex.PackageOf(from), def); ok { + return b.external(key) + } + return def +} + +// symbolKey returns the external key of a definition of a unit of +// dependency code, as an import of its package names it, and records the +// import as an origin of the key for a file of the importer unit. +func (b *builder) symbolKey(importer, def string) (string, bool) { + unit := fileindex.PackageOf(def) + sp := b.spaces[unit] + if unit == "" || sp == nil { + return "", false + } + lang := b.languageOf(def) + symbol := profileOf(lang.Family()).symbol + if symbol == nil { + return "", false + } + module, names, ok := symbol(b, sp, def) + if !ok { + return "", false + } + key := fileindex.Join(fileindex.Namespace(lang, module), names...) + o := origin{lang: lang, unit: importer, module: module, form: modules.FormImport, names: names} + if !slices.ContainsFunc(b.origins[key], o.equal) { + b.origins[key] = append(b.origins[key], o) + } + return key, true +} + +// importedWalk resolves the names under an import that no file of the +// space of the importer gives, as html//NewTokenizer//Next for +// html.NewTokenizer(r).Next(). The walk reads the names one at a time from +// the key of the module, so the result type of a function that a unit of +// dependency code gives types the next name. +func (b *builder) importedWalk(ix *fileindex.Index, target, module string, form modules.Form, names []string) resolution { + base, ok := strings.CutSuffix(target, fileindex.Join("", names...)) + if len(names) < 2 || !ok { + return resolution{key: b.importedExternal(ix, target, module, form, names), kind: EdgeDirect, rule: ruleExternal} + } + b.recordOrigin(ix, base, module, form, nil) + return b.walkFrom(resolution{key: base, kind: EdgeDirect, rule: ruleExternal}, names) +} diff --git a/projectgraph/testdata/javascript/barrel-chain/graph.golden.json b/projectgraph/testdata/javascript/barrel-chain/graph.golden.json index 85867615..7aa9f7d9 100644 --- a/projectgraph/testdata/javascript/barrel-chain/graph.golden.json +++ b/projectgraph/testdata/javascript/barrel-chain/graph.golden.json @@ -174,7 +174,7 @@ "src/main.js": "indexed" }, "stats": { - "version": "12", + "version": "13", "files": 4, "nodes": 7, "edges": 6, diff --git a/projectgraph/testdata/javascript/barrel-chain/indexes.json b/projectgraph/testdata/javascript/barrel-chain/indexes.json index 2f008821..75d25f01 100644 --- a/projectgraph/testdata/javascript/barrel-chain/indexes.json +++ b/projectgraph/testdata/javascript/barrel-chain/indexes.json @@ -1,22 +1,22 @@ [ { - "version": "12", "path": "src/main.js", "language": "javascript", + "version": "13", "path": "src/main.js", "language": "javascript", "imports": [{"line": 1, "module": "./api", "item": "parse", "alias": "parse", "kind": "static", "namespace": ".//api//parse"}], "definitions": [{"key": "src/main.js", "kind": "module", "name": "main", "start_line": 1, "end_line": 2, "exported": true}], "calls": [{"caller": "src/main.js", "line": 2, "column": 1, "callee": "parse", "targets": [".//api//parse"], "kind": "call"}] }, { - "version": "12", "path": "src/api/index.js", "language": "javascript", + "version": "13", "path": "src/api/index.js", "language": "javascript", "exports": [{"name": "parse", "target": "", "from": "../core", "item": "parse", "line": 1}], "definitions": [{"key": "src/api/index.js", "kind": "module", "name": "index", "start_line": 1, "end_line": 1, "exported": true}] }, { - "version": "12", "path": "src/core/index.js", "language": "javascript", + "version": "13", "path": "src/core/index.js", "language": "javascript", "exports": [{"name": "parse", "target": "", "from": "./parse", "item": "doParse", "line": 1}], "definitions": [{"key": "src/core/index.js", "kind": "module", "name": "index", "start_line": 1, "end_line": 1, "exported": true}] }, { - "version": "12", "path": "src/core/parse.js", "language": "javascript", + "version": "13", "path": "src/core/parse.js", "language": "javascript", "exports": [{"name": "doParse", "target": "src/core/parse.js//doParse", "line": 1}], "definitions": [ {"key": "src/core/parse.js", "kind": "module", "name": "parse", "start_line": 1, "end_line": 3, "exported": true}, diff --git a/projectgraph/testdata/javascript/commonjs-require/graph.golden.json b/projectgraph/testdata/javascript/commonjs-require/graph.golden.json index c6fd6b39..df340c72 100644 --- a/projectgraph/testdata/javascript/commonjs-require/graph.golden.json +++ b/projectgraph/testdata/javascript/commonjs-require/graph.golden.json @@ -138,7 +138,7 @@ "main.js": "indexed" }, "stats": { - "version": "12", + "version": "13", "files": 3, "nodes": 5, "edges": 4, diff --git a/projectgraph/testdata/javascript/commonjs-require/indexes.json b/projectgraph/testdata/javascript/commonjs-require/indexes.json index 9ca52585..984ac4c2 100644 --- a/projectgraph/testdata/javascript/commonjs-require/indexes.json +++ b/projectgraph/testdata/javascript/commonjs-require/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "12", "path": "main.js", "language": "javascript", + "version": "13", "path": "main.js", "language": "javascript", "imports": [ {"line": 1, "module": "./lib", "alias": "utils", "kind": "static", "namespace": ".//lib"}, {"line": 2, "module": "./helper", "item": "helper", "alias": "helper", "kind": "static", "namespace": ".//helper//helper"} @@ -12,7 +12,7 @@ ] }, { - "version": "12", "path": "lib/index.js", "language": "javascript", + "version": "13", "path": "lib/index.js", "language": "javascript", "exports": [{"name": "run", "target": "lib/index.js//run", "line": 3}], "definitions": [ {"key": "lib/index.js", "kind": "module", "name": "index", "start_line": 1, "end_line": 3, "exported": true}, @@ -20,7 +20,7 @@ ] }, { - "version": "12", "path": "helper.js", "language": "javascript", + "version": "13", "path": "helper.js", "language": "javascript", "exports": [{"name": "helper", "target": "helper.js//helper", "line": 3}], "definitions": [ {"key": "helper.js", "kind": "module", "name": "helper", "start_line": 1, "end_line": 3, "exported": true}, diff --git a/projectgraph/testdata/javascript/default-import/graph.golden.json b/projectgraph/testdata/javascript/default-import/graph.golden.json index 1ff1e467..9d90e497 100644 --- a/projectgraph/testdata/javascript/default-import/graph.golden.json +++ b/projectgraph/testdata/javascript/default-import/graph.golden.json @@ -138,7 +138,7 @@ "make.js": "indexed" }, "stats": { - "version": "12", + "version": "13", "files": 3, "nodes": 5, "edges": 4, diff --git a/projectgraph/testdata/javascript/default-import/indexes.json b/projectgraph/testdata/javascript/default-import/indexes.json index 8e5bbad0..2c2028e5 100644 --- a/projectgraph/testdata/javascript/default-import/indexes.json +++ b/projectgraph/testdata/javascript/default-import/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "12", "path": "main.js", "language": "javascript", + "version": "13", "path": "main.js", "language": "javascript", "imports": [ {"line": 1, "module": "./log", "alias": "log", "kind": "static", "namespace": ".//log"}, {"line": 2, "module": "./make", "alias": "make", "kind": "static", "namespace": ".//make"} @@ -12,7 +12,7 @@ ] }, { - "version": "12", "path": "log.js", "language": "javascript", + "version": "13", "path": "log.js", "language": "javascript", "exports": [{"name": "default", "target": "log.js//log", "line": 1}], "definitions": [ {"key": "log.js", "kind": "module", "name": "log", "start_line": 1, "end_line": 1, "exported": true}, @@ -20,7 +20,7 @@ ] }, { - "version": "12", "path": "make.js", "language": "javascript", + "version": "13", "path": "make.js", "language": "javascript", "exports": [{"name": "default", "target": "make.js//make", "line": 1}], "definitions": [ {"key": "make.js", "kind": "module", "name": "make", "start_line": 1, "end_line": 1, "exported": true}, diff --git a/projectgraph/testdata/javascript/esm-named-import/graph.golden.json b/projectgraph/testdata/javascript/esm-named-import/graph.golden.json index db42551c..6b3b5b33 100644 --- a/projectgraph/testdata/javascript/esm-named-import/graph.golden.json +++ b/projectgraph/testdata/javascript/esm-named-import/graph.golden.json @@ -166,7 +166,7 @@ "src/util.js": "indexed" }, "stats": { - "version": "12", + "version": "13", "files": 3, "nodes": 6, "edges": 5, diff --git a/projectgraph/testdata/javascript/esm-named-import/indexes.json b/projectgraph/testdata/javascript/esm-named-import/indexes.json index 88700bac..1a9c0a0c 100644 --- a/projectgraph/testdata/javascript/esm-named-import/indexes.json +++ b/projectgraph/testdata/javascript/esm-named-import/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "12", "path": "src/main.js", "language": "javascript", + "version": "13", "path": "src/main.js", "language": "javascript", "imports": [ {"line": 1, "module": "./parser.js", "item": "parse", "alias": "parse", "kind": "static", "namespace": ".//parser.js//parse"}, {"line": 2, "module": "./util", "item": "fmt", "alias": "fmt", "kind": "static", "namespace": ".//util//fmt"}, @@ -14,7 +14,7 @@ "unresolved": [{"caller": "src/main.js", "line": 5, "column": 1, "text": "import", "reason": "dynamic-import"}] }, { - "version": "12", "path": "src/parser.js", "language": "javascript", + "version": "13", "path": "src/parser.js", "language": "javascript", "imports": [{"line": 1, "module": "js-yaml", "alias": "yaml", "kind": "static", "namespace": "js-yaml"}], "exports": [{"name": "parse", "target": "src/parser.js//parse", "line": 2}], "definitions": [ @@ -24,7 +24,7 @@ "calls": [{"caller": "src/parser.js//parse", "line": 3, "column": 10, "callee": "yaml.load", "targets": ["js-yaml//load"], "receiver": "yaml", "method": "load", "kind": "call"}] }, { - "version": "12", "path": "src/util.js", "language": "javascript", + "version": "13", "path": "src/util.js", "language": "javascript", "exports": [{"name": "fmt", "target": "src/util.js//fmt", "line": 1}], "definitions": [ {"key": "src/util.js", "kind": "module", "name": "util", "start_line": 1, "end_line": 1, "exported": true}, diff --git a/projectgraph/testdata/javascript/export-star/graph.golden.json b/projectgraph/testdata/javascript/export-star/graph.golden.json index dd771d0f..91054813 100644 --- a/projectgraph/testdata/javascript/export-star/graph.golden.json +++ b/projectgraph/testdata/javascript/export-star/graph.golden.json @@ -276,7 +276,7 @@ "main.js": "indexed" }, "stats": { - "version": "12", + "version": "13", "files": 5, "nodes": 11, "edges": 10, diff --git a/projectgraph/testdata/javascript/export-star/indexes.json b/projectgraph/testdata/javascript/export-star/indexes.json index 58526a72..47e8dd47 100644 --- a/projectgraph/testdata/javascript/export-star/indexes.json +++ b/projectgraph/testdata/javascript/export-star/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "12", "path": "main.js", "language": "javascript", + "version": "13", "path": "main.js", "language": "javascript", "imports": [ {"line": 1, "module": "./all", "item": "a", "alias": "a", "kind": "static", "namespace": ".//all//a"}, {"line": 1, "module": "./all", "item": "b", "alias": "b", "kind": "static", "namespace": ".//all//b"}, @@ -14,7 +14,7 @@ ] }, { - "version": "12", "path": "all.js", "language": "javascript", + "version": "13", "path": "all.js", "language": "javascript", "exports": [ {"name": "*", "target": "", "from": "./a", "item": "*", "line": 1}, {"name": "*", "target": "", "from": "./b", "item": "*", "line": 2}, @@ -23,7 +23,7 @@ "definitions": [{"key": "all.js", "kind": "module", "name": "all", "start_line": 1, "end_line": 3, "exported": true}] }, { - "version": "12", "path": "a.js", "language": "javascript", + "version": "13", "path": "a.js", "language": "javascript", "exports": [{"name": "a", "target": "a.js//a", "line": 1}], "definitions": [ {"key": "a.js", "kind": "module", "name": "a", "start_line": 1, "end_line": 1, "exported": true}, @@ -31,7 +31,7 @@ ] }, { - "version": "12", "path": "b.js", "language": "javascript", + "version": "13", "path": "b.js", "language": "javascript", "exports": [{"name": "b", "target": "b.js//b", "line": 1}], "definitions": [ {"key": "b.js", "kind": "module", "name": "b", "start_line": 1, "end_line": 1, "exported": true}, @@ -39,7 +39,7 @@ ] }, { - "version": "12", "path": "c.js", "language": "javascript", + "version": "13", "path": "c.js", "language": "javascript", "exports": [{"name": "c", "target": "c.js//c", "line": 1}], "definitions": [ {"key": "c.js", "kind": "module", "name": "c", "start_line": 1, "end_line": 1, "exported": true}, diff --git a/projectgraph/testdata/javascript/exports-conditions/graph.golden.json b/projectgraph/testdata/javascript/exports-conditions/graph.golden.json index aabf0476..633ebc58 100644 --- a/projectgraph/testdata/javascript/exports-conditions/graph.golden.json +++ b/projectgraph/testdata/javascript/exports-conditions/graph.golden.json @@ -144,7 +144,7 @@ "lib/src/feature/y.js": "indexed" }, "stats": { - "version": "12", + "version": "13", "files": 4, "nodes": 7, "edges": 4, diff --git a/projectgraph/testdata/javascript/exports-conditions/indexes.json b/projectgraph/testdata/javascript/exports-conditions/indexes.json index 823785e0..534682a6 100644 --- a/projectgraph/testdata/javascript/exports-conditions/indexes.json +++ b/projectgraph/testdata/javascript/exports-conditions/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "12", "path": "app/main.mjs", "language": "javascript", + "version": "13", "path": "app/main.mjs", "language": "javascript", "imports": [ {"line": 1, "module": "@acme/lib", "item": "x", "alias": "x", "kind": "static", "namespace": "@acme//lib//x"}, {"line": 2, "module": "@acme/lib/feature/y", "item": "y", "alias": "y", "kind": "static", "namespace": "@acme//lib//feature//y//y"} @@ -12,7 +12,7 @@ ] }, { - "version": "12", "path": "lib/esm/index.mjs", "language": "javascript", + "version": "13", "path": "lib/esm/index.mjs", "language": "javascript", "exports": [{"name": "x", "target": "lib/esm/index.mjs//x", "line": 1}], "definitions": [ {"key": "lib/esm/index.mjs", "kind": "module", "name": "index", "start_line": 1, "end_line": 1, "exported": true}, @@ -20,7 +20,7 @@ ] }, { - "version": "12", "path": "lib/cjs/index.cjs", "language": "javascript", + "version": "13", "path": "lib/cjs/index.cjs", "language": "javascript", "exports": [{"name": "x", "target": "lib/cjs/index.cjs//x", "line": 1}], "definitions": [ {"key": "lib/cjs/index.cjs", "kind": "module", "name": "index", "start_line": 1, "end_line": 1, "exported": true}, @@ -28,7 +28,7 @@ ] }, { - "version": "12", "path": "lib/src/feature/y.js", "language": "javascript", + "version": "13", "path": "lib/src/feature/y.js", "language": "javascript", "exports": [{"name": "y", "target": "lib/src/feature/y.js//y", "line": 1}], "definitions": [ {"key": "lib/src/feature/y.js", "kind": "module", "name": "y", "start_line": 1, "end_line": 1, "exported": true}, diff --git a/projectgraph/testdata/javascript/unknown-receiver/graph.golden.json b/projectgraph/testdata/javascript/unknown-receiver/graph.golden.json index bef80997..4e9329bc 100644 --- a/projectgraph/testdata/javascript/unknown-receiver/graph.golden.json +++ b/projectgraph/testdata/javascript/unknown-receiver/graph.golden.json @@ -124,7 +124,7 @@ "main.js": "indexed" }, "stats": { - "version": "12", + "version": "13", "files": 1, "nodes": 5, "edges": 3, diff --git a/projectgraph/testdata/javascript/unknown-receiver/indexes.json b/projectgraph/testdata/javascript/unknown-receiver/indexes.json index bba27ba3..bc462192 100644 --- a/projectgraph/testdata/javascript/unknown-receiver/indexes.json +++ b/projectgraph/testdata/javascript/unknown-receiver/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "12", + "version": "13", "path": "main.js", "language": "javascript", "imports": [ diff --git a/projectgraph/testdata/javascript/workspaces/graph.golden.json b/projectgraph/testdata/javascript/workspaces/graph.golden.json index 2ba1864e..c0b79688 100644 --- a/projectgraph/testdata/javascript/workspaces/graph.golden.json +++ b/projectgraph/testdata/javascript/workspaces/graph.golden.json @@ -127,7 +127,7 @@ "packages/core/util.js": "indexed" }, "stats": { - "version": "12", + "version": "13", "files": 3, "nodes": 6, "edges": 5, diff --git a/projectgraph/testdata/javascript/workspaces/indexes.json b/projectgraph/testdata/javascript/workspaces/indexes.json index a6137edb..f534f937 100644 --- a/projectgraph/testdata/javascript/workspaces/indexes.json +++ b/projectgraph/testdata/javascript/workspaces/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "12", "path": "packages/app/src/main.js", "language": "javascript", + "version": "13", "path": "packages/app/src/main.js", "language": "javascript", "imports": [ {"line": 1, "module": "@acme/core", "item": "start", "alias": "start", "kind": "static", "namespace": "@acme//core//start"}, {"line": 2, "module": "@acme/core/util", "item": "tidy", "alias": "tidy", "kind": "static", "namespace": "@acme//core//util//tidy"}, @@ -14,7 +14,7 @@ ] }, { - "version": "12", "path": "packages/core/lib/index.js", "language": "javascript", + "version": "13", "path": "packages/core/lib/index.js", "language": "javascript", "exports": [{"name": "start", "target": "packages/core/lib/index.js//start", "line": 1}], "definitions": [ {"key": "packages/core/lib/index.js", "kind": "module", "name": "index", "start_line": 1, "end_line": 1, "exported": true}, @@ -22,7 +22,7 @@ ] }, { - "version": "12", "path": "packages/core/util.js", "language": "javascript", + "version": "13", "path": "packages/core/util.js", "language": "javascript", "exports": [{"name": "tidy", "target": "packages/core/util.js//tidy", "line": 1}], "definitions": [ {"key": "packages/core/util.js", "kind": "module", "name": "util", "start_line": 1, "end_line": 1, "exported": true}, diff --git a/projectgraph/testdata/python/all-hides/graph.golden.json b/projectgraph/testdata/python/all-hides/graph.golden.json index 189e173f..f763eb3e 100644 --- a/projectgraph/testdata/python/all-hides/graph.golden.json +++ b/projectgraph/testdata/python/all-hides/graph.golden.json @@ -174,7 +174,7 @@ "pkg/a.py": "indexed" }, "stats": { - "version": "12", + "version": "13", "files": 3, "nodes": 7, "edges": 6, diff --git a/projectgraph/testdata/python/all-hides/indexes.json b/projectgraph/testdata/python/all-hides/indexes.json index 62a1d789..5f3e1202 100644 --- a/projectgraph/testdata/python/all-hides/indexes.json +++ b/projectgraph/testdata/python/all-hides/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "12", "path": "main.py", "language": "python", + "version": "13", "path": "main.py", "language": "python", "imports": [ {"line": 1, "module": "pkg", "item": "f", "alias": "f", "kind": "static", "namespace": "pkg//f"}, {"line": 1, "module": "pkg", "item": "g", "alias": "g", "kind": "static", "namespace": "pkg//g"} @@ -12,7 +12,7 @@ ] }, { - "version": "12", "path": "pkg/__init__.py", "language": "python", + "version": "13", "path": "pkg/__init__.py", "language": "python", "imports": [ {"line": 1, "module": ".a", "item": "f", "alias": "f", "kind": "static", "namespace": "//a//f"}, {"line": 1, "module": ".a", "item": "g", "alias": "g", "kind": "static", "namespace": "//a//g"} @@ -21,7 +21,7 @@ "definitions": [{"key": "pkg/__init__.py", "kind": "module", "name": "__init__", "start_line": 1, "end_line": 2, "exported": true}] }, { - "version": "12", "path": "pkg/a.py", "language": "python", + "version": "13", "path": "pkg/a.py", "language": "python", "definitions": [ {"key": "pkg/a.py", "kind": "module", "name": "a", "start_line": 1, "end_line": 4, "exported": true}, {"key": "pkg/a.py//f", "kind": "function", "name": "f", "start_line": 1, "end_line": 2, "exported": true}, diff --git a/projectgraph/testdata/python/assignment/graph.golden.json b/projectgraph/testdata/python/assignment/graph.golden.json index 5112173b..3c9f53e8 100644 --- a/projectgraph/testdata/python/assignment/graph.golden.json +++ b/projectgraph/testdata/python/assignment/graph.golden.json @@ -70,7 +70,7 @@ "main.py": "indexed" }, "stats": { - "version": "12", + "version": "13", "files": 1, "nodes": 3, "edges": 2, diff --git a/projectgraph/testdata/python/assignment/indexes.json b/projectgraph/testdata/python/assignment/indexes.json index c89aa26b..75165aa4 100644 --- a/projectgraph/testdata/python/assignment/indexes.json +++ b/projectgraph/testdata/python/assignment/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "12", "path": "main.py", "language": "python", + "version": "13", "path": "main.py", "language": "python", "imports": [{"line": 1, "module": "yaml", "alias": "yaml", "kind": "static", "namespace": "yaml"}], "definitions": [ {"key": "main.py", "kind": "module", "name": "main", "start_line": 1, "end_line": 7, "exported": true}, diff --git a/projectgraph/testdata/python/class-body/graph.golden.json b/projectgraph/testdata/python/class-body/graph.golden.json index abe0026d..bfe7cf9c 100644 --- a/projectgraph/testdata/python/class-body/graph.golden.json +++ b/projectgraph/testdata/python/class-body/graph.golden.json @@ -224,7 +224,7 @@ "config.py": "indexed" }, "stats": { - "version": "12", + "version": "13", "files": 1, "nodes": 12, "edges": 7, diff --git a/projectgraph/testdata/python/class-body/indexes.json b/projectgraph/testdata/python/class-body/indexes.json index 6c7fc685..c9f20b9f 100644 --- a/projectgraph/testdata/python/class-body/indexes.json +++ b/projectgraph/testdata/python/class-body/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "12", + "version": "13", "path": "config.py", "language": "python", "imports": [ diff --git a/projectgraph/testdata/python/decorators/graph.golden.json b/projectgraph/testdata/python/decorators/graph.golden.json index 760f11a4..0a6e818f 100644 --- a/projectgraph/testdata/python/decorators/graph.golden.json +++ b/projectgraph/testdata/python/decorators/graph.golden.json @@ -194,7 +194,7 @@ "app.py": "indexed" }, "stats": { - "version": "12", + "version": "13", "files": 1, "nodes": 9, "edges": 5, diff --git a/projectgraph/testdata/python/decorators/indexes.json b/projectgraph/testdata/python/decorators/indexes.json index 959f8ce3..1cf6be87 100644 --- a/projectgraph/testdata/python/decorators/indexes.json +++ b/projectgraph/testdata/python/decorators/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "12", + "version": "13", "path": "app.py", "language": "python", "imports": [ diff --git a/projectgraph/testdata/python/external/graph.golden.json b/projectgraph/testdata/python/external/graph.golden.json index 52362e5d..18683074 100644 --- a/projectgraph/testdata/python/external/graph.golden.json +++ b/projectgraph/testdata/python/external/graph.golden.json @@ -108,7 +108,7 @@ "main.py": "indexed" }, "stats": { - "version": "12", + "version": "13", "files": 1, "nodes": 5, "edges": 3, diff --git a/projectgraph/testdata/python/external/indexes.json b/projectgraph/testdata/python/external/indexes.json index ae85c90b..a2b4bd88 100644 --- a/projectgraph/testdata/python/external/indexes.json +++ b/projectgraph/testdata/python/external/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "12", "path": "main.py", "language": "python", + "version": "13", "path": "main.py", "language": "python", "imports": [ {"line": 1, "module": "requests", "alias": "requests", "kind": "static", "namespace": "requests"}, {"line": 2, "module": "yaml", "item": "load", "alias": "yl", "kind": "static", "namespace": "yaml//load"}, diff --git a/projectgraph/testdata/python/local-receiver/graph.golden.json b/projectgraph/testdata/python/local-receiver/graph.golden.json index 7d03ed8f..e0b12b9d 100644 --- a/projectgraph/testdata/python/local-receiver/graph.golden.json +++ b/projectgraph/testdata/python/local-receiver/graph.golden.json @@ -145,7 +145,7 @@ "main.py": "indexed" }, "stats": { - "version": "12", + "version": "13", "files": 1, "nodes": 5, "edges": 3, diff --git a/projectgraph/testdata/python/local-receiver/indexes.json b/projectgraph/testdata/python/local-receiver/indexes.json index 29c0ed1d..adc5e30e 100644 --- a/projectgraph/testdata/python/local-receiver/indexes.json +++ b/projectgraph/testdata/python/local-receiver/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "12", + "version": "13", "path": "main.py", "language": "python", "imports": [ diff --git a/projectgraph/testdata/python/member-call/graph.golden.json b/projectgraph/testdata/python/member-call/graph.golden.json index f39823b0..b4e08c6c 100644 --- a/projectgraph/testdata/python/member-call/graph.golden.json +++ b/projectgraph/testdata/python/member-call/graph.golden.json @@ -258,7 +258,7 @@ "parser.py": "indexed" }, "stats": { - "version": "12", + "version": "13", "files": 3, "nodes": 9, "edges": 10, diff --git a/projectgraph/testdata/python/member-call/indexes.json b/projectgraph/testdata/python/member-call/indexes.json index e451c9c9..3b73b713 100644 --- a/projectgraph/testdata/python/member-call/indexes.json +++ b/projectgraph/testdata/python/member-call/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "12", "path": "main.py", "language": "python", + "version": "13", "path": "main.py", "language": "python", "imports": [{"line": 1, "module": "parser", "item": "Parser", "alias": "Parser", "kind": "static", "namespace": "parser//Parser"}], "definitions": [{"key": "main.py", "kind": "module", "name": "main", "start_line": 1, "end_line": 4, "exported": true}], "calls": [ @@ -10,7 +10,7 @@ ] }, { - "version": "12", "path": "parser.py", "language": "python", + "version": "13", "path": "parser.py", "language": "python", "imports": [{"line": 1, "module": "base", "item": "Base", "alias": "Base", "kind": "static", "namespace": "base//Base"}], "definitions": [ {"key": "parser.py", "kind": "module", "name": "parser", "start_line": 1, "end_line": 7, "exported": true}, @@ -22,7 +22,7 @@ "calls": [{"caller": "parser.py//Parser//parse", "line": 6, "column": 9, "callee": "self.helper", "targets": ["parser.py//Parser//helper"], "receiver": "self", "method": "helper", "kind": "call"}] }, { - "version": "12", "path": "base.py", "language": "python", + "version": "13", "path": "base.py", "language": "python", "imports": [{"line": 1, "module": "io", "alias": "io", "kind": "static", "namespace": "io"}], "definitions": [ {"key": "base.py", "kind": "module", "name": "base", "start_line": 1, "end_line": 4, "exported": true}, diff --git a/projectgraph/testdata/python/missing-file/graph.golden.json b/projectgraph/testdata/python/missing-file/graph.golden.json index 68d7771e..5ea99fbd 100644 --- a/projectgraph/testdata/python/missing-file/graph.golden.json +++ b/projectgraph/testdata/python/missing-file/graph.golden.json @@ -55,7 +55,7 @@ "main.py": "cached" }, "stats": { - "version": "12", + "version": "13", "files": 2, "nodes": 2, "edges": 2, diff --git a/projectgraph/testdata/python/missing-file/indexes.json b/projectgraph/testdata/python/missing-file/indexes.json index 59c334be..e27a4cf5 100644 --- a/projectgraph/testdata/python/missing-file/indexes.json +++ b/projectgraph/testdata/python/missing-file/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "12", "path": "main.py", "language": "python", + "version": "13", "path": "main.py", "language": "python", "imports": [{"line": 1, "module": "handlers", "item": "parse", "alias": "parse", "kind": "static", "namespace": "handlers//parse"}], "definitions": [{"key": "main.py", "kind": "module", "name": "main", "start_line": 1, "end_line": 2, "exported": true}], "calls": [{"caller": "main.py", "line": 2, "column": 1, "callee": "parse", "targets": ["handlers//parse"], "kind": "call"}] diff --git a/projectgraph/testdata/python/package-init/graph.golden.json b/projectgraph/testdata/python/package-init/graph.golden.json index 073ac72e..04bd929e 100644 --- a/projectgraph/testdata/python/package-init/graph.golden.json +++ b/projectgraph/testdata/python/package-init/graph.golden.json @@ -144,7 +144,7 @@ "pkg/loader.py": "indexed" }, "stats": { - "version": "12", + "version": "13", "files": 3, "nodes": 6, "edges": 5, diff --git a/projectgraph/testdata/python/package-init/indexes.json b/projectgraph/testdata/python/package-init/indexes.json index 2f2aa9a3..e840dd3c 100644 --- a/projectgraph/testdata/python/package-init/indexes.json +++ b/projectgraph/testdata/python/package-init/indexes.json @@ -1,17 +1,17 @@ [ { - "version": "12", "path": "main.py", "language": "python", + "version": "13", "path": "main.py", "language": "python", "imports": [{"line": 1, "module": "pkg", "item": "load", "alias": "load", "kind": "static", "namespace": "pkg//load"}], "definitions": [{"key": "main.py", "kind": "module", "name": "main", "start_line": 1, "end_line": 2, "exported": true}], "calls": [{"caller": "main.py", "line": 2, "column": 1, "callee": "load", "targets": ["pkg//load"], "kind": "call"}] }, { - "version": "12", "path": "pkg/__init__.py", "language": "python", + "version": "13", "path": "pkg/__init__.py", "language": "python", "imports": [{"line": 1, "module": ".loader", "item": "load", "alias": "load", "kind": "static", "namespace": "//loader//load"}], "definitions": [{"key": "pkg/__init__.py", "kind": "module", "name": "__init__", "start_line": 1, "end_line": 1, "exported": true}] }, { - "version": "12", "path": "pkg/loader.py", "language": "python", + "version": "13", "path": "pkg/loader.py", "language": "python", "imports": [{"line": 1, "module": "yaml", "alias": "yaml", "kind": "static", "namespace": "yaml"}], "definitions": [ {"key": "pkg/loader.py", "kind": "module", "name": "loader", "start_line": 1, "end_line": 4, "exported": true}, diff --git a/projectgraph/testdata/python/reexport-cycle/graph.golden.json b/projectgraph/testdata/python/reexport-cycle/graph.golden.json index 1ee0ecb6..e52ae5b7 100644 --- a/projectgraph/testdata/python/reexport-cycle/graph.golden.json +++ b/projectgraph/testdata/python/reexport-cycle/graph.golden.json @@ -105,7 +105,7 @@ "main.py": "indexed" }, "stats": { - "version": "12", + "version": "13", "files": 3, "nodes": 3, "edges": 3, diff --git a/projectgraph/testdata/python/reexport-cycle/indexes.json b/projectgraph/testdata/python/reexport-cycle/indexes.json index ed02d876..088fee96 100644 --- a/projectgraph/testdata/python/reexport-cycle/indexes.json +++ b/projectgraph/testdata/python/reexport-cycle/indexes.json @@ -1,17 +1,17 @@ [ { - "version": "12", "path": "main.py", "language": "python", + "version": "13", "path": "main.py", "language": "python", "imports": [{"line": 1, "module": "a", "item": "x", "alias": "x", "kind": "static", "namespace": "a//x"}], "definitions": [{"key": "main.py", "kind": "module", "name": "main", "start_line": 1, "end_line": 2, "exported": true}], "calls": [{"caller": "main.py", "line": 2, "column": 1, "callee": "x", "targets": ["a//x"], "kind": "call"}] }, { - "version": "12", "path": "a/__init__.py", "language": "python", + "version": "13", "path": "a/__init__.py", "language": "python", "imports": [{"line": 1, "module": "b", "item": "x", "alias": "x", "kind": "static", "namespace": "b//x"}], "definitions": [{"key": "a/__init__.py", "kind": "module", "name": "__init__", "start_line": 1, "end_line": 1, "exported": true}] }, { - "version": "12", "path": "b/__init__.py", "language": "python", + "version": "13", "path": "b/__init__.py", "language": "python", "imports": [{"line": 1, "module": "a", "item": "x", "alias": "x", "kind": "static", "namespace": "a//x"}], "definitions": [{"key": "b/__init__.py", "kind": "module", "name": "__init__", "start_line": 1, "end_line": 1, "exported": true}] } diff --git a/projectgraph/testdata/python/relative-imports/graph.golden.json b/projectgraph/testdata/python/relative-imports/graph.golden.json index 93e6bfe3..8edf0567 100644 --- a/projectgraph/testdata/python/relative-imports/graph.golden.json +++ b/projectgraph/testdata/python/relative-imports/graph.golden.json @@ -154,7 +154,7 @@ "pkg/sub/b.py": "indexed" }, "stats": { - "version": "12", + "version": "13", "files": 3, "nodes": 6, "edges": 4, diff --git a/projectgraph/testdata/python/relative-imports/indexes.json b/projectgraph/testdata/python/relative-imports/indexes.json index c33fb94a..4248be64 100644 --- a/projectgraph/testdata/python/relative-imports/indexes.json +++ b/projectgraph/testdata/python/relative-imports/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "12", "path": "pkg/sub/a.py", "language": "python", + "version": "13", "path": "pkg/sub/a.py", "language": "python", "imports": [ {"line": 1, "module": ".b", "item": "f", "alias": "f", "kind": "static", "namespace": "//b//f"}, {"line": 2, "module": "..c", "item": "g", "alias": "g", "kind": "static", "namespace": "////c//g"} @@ -15,14 +15,14 @@ ] }, { - "version": "12", "path": "pkg/sub/b.py", "language": "python", + "version": "13", "path": "pkg/sub/b.py", "language": "python", "definitions": [ {"key": "pkg/sub/b.py", "kind": "module", "name": "b", "start_line": 1, "end_line": 2, "exported": true}, {"key": "pkg/sub/b.py//f", "kind": "function", "name": "f", "start_line": 1, "end_line": 2, "exported": true} ] }, { - "version": "12", "path": "pkg/c.py", "language": "python", + "version": "13", "path": "pkg/c.py", "language": "python", "definitions": [ {"key": "pkg/c.py", "kind": "module", "name": "c", "start_line": 1, "end_line": 2, "exported": true}, {"key": "pkg/c.py//g", "kind": "function", "name": "g", "start_line": 1, "end_line": 2, "exported": true} diff --git a/projectgraph/testdata/python/sibling-import/graph.golden.json b/projectgraph/testdata/python/sibling-import/graph.golden.json index 4dd25819..9f033009 100644 --- a/projectgraph/testdata/python/sibling-import/graph.golden.json +++ b/projectgraph/testdata/python/sibling-import/graph.golden.json @@ -98,7 +98,7 @@ "main.py": "indexed" }, "stats": { - "version": "12", + "version": "13", "files": 2, "nodes": 4, "edges": 3, diff --git a/projectgraph/testdata/python/sibling-import/indexes.json b/projectgraph/testdata/python/sibling-import/indexes.json index 8b3e383b..790eba1a 100644 --- a/projectgraph/testdata/python/sibling-import/indexes.json +++ b/projectgraph/testdata/python/sibling-import/indexes.json @@ -1,12 +1,12 @@ [ { - "version": "12", "path": "main.py", "language": "python", + "version": "13", "path": "main.py", "language": "python", "imports": [{"line": 1, "module": "handlers", "item": "parse", "alias": "parse", "kind": "static", "namespace": "handlers//parse"}], "definitions": [{"key": "main.py", "kind": "module", "name": "main", "start_line": 1, "end_line": 3, "exported": true}], "calls": [{"caller": "main.py", "line": 3, "column": 1, "callee": "parse", "targets": ["handlers//parse"], "kind": "call"}] }, { - "version": "12", "path": "handlers.py", "language": "python", + "version": "13", "path": "handlers.py", "language": "python", "imports": [{"line": 1, "module": "yaml", "alias": "yaml", "kind": "static", "namespace": "yaml"}], "definitions": [ {"key": "handlers.py", "kind": "module", "name": "handlers", "start_line": 1, "end_line": 4, "exported": true}, diff --git a/projectgraph/testdata/python/src-layout/graph.golden.json b/projectgraph/testdata/python/src-layout/graph.golden.json index d402f333..1c8dcf06 100644 --- a/projectgraph/testdata/python/src-layout/graph.golden.json +++ b/projectgraph/testdata/python/src-layout/graph.golden.json @@ -99,7 +99,7 @@ "services/api/src/api/core.py": "indexed" }, "stats": { - "version": "12", + "version": "13", "files": 3, "nodes": 4, "edges": 2, diff --git a/projectgraph/testdata/python/src-layout/indexes.json b/projectgraph/testdata/python/src-layout/indexes.json index 1092ace6..74817fae 100644 --- a/projectgraph/testdata/python/src-layout/indexes.json +++ b/projectgraph/testdata/python/src-layout/indexes.json @@ -1,16 +1,16 @@ [ { - "version": "12", "path": "services/api/src/api/__init__.py", "language": "python", + "version": "13", "path": "services/api/src/api/__init__.py", "language": "python", "definitions": [{"key": "services/api/src/api/__init__.py", "kind": "module", "name": "__init__", "start_line": 1, "end_line": 1, "exported": true}] }, { - "version": "12", "path": "services/api/src/api/cli.py", "language": "python", + "version": "13", "path": "services/api/src/api/cli.py", "language": "python", "imports": [{"line": 1, "module": "api.core", "item": "run", "alias": "run", "kind": "static", "namespace": "api//core//run"}], "definitions": [{"key": "services/api/src/api/cli.py", "kind": "module", "name": "cli", "start_line": 1, "end_line": 2, "exported": true}], "calls": [{"caller": "services/api/src/api/cli.py", "line": 2, "column": 1, "callee": "run", "targets": ["api//core//run"], "kind": "call"}] }, { - "version": "12", "path": "services/api/src/api/core.py", "language": "python", + "version": "13", "path": "services/api/src/api/core.py", "language": "python", "definitions": [ {"key": "services/api/src/api/core.py", "kind": "module", "name": "core", "start_line": 1, "end_line": 2, "exported": true}, {"key": "services/api/src/api/core.py//run", "kind": "function", "name": "run", "start_line": 1, "end_line": 2, "exported": true} diff --git a/projectgraph/testdata/python/sys-path/graph.golden.json b/projectgraph/testdata/python/sys-path/graph.golden.json index 4e5b44bd..42aaf46a 100644 --- a/projectgraph/testdata/python/sys-path/graph.golden.json +++ b/projectgraph/testdata/python/sys-path/graph.golden.json @@ -82,7 +82,7 @@ "lib/shared.py": "indexed" }, "stats": { - "version": "12", + "version": "13", "files": 2, "nodes": 3, "edges": 2, diff --git a/projectgraph/testdata/python/sys-path/indexes.json b/projectgraph/testdata/python/sys-path/indexes.json index 3833cba8..b4c7270b 100644 --- a/projectgraph/testdata/python/sys-path/indexes.json +++ b/projectgraph/testdata/python/sys-path/indexes.json @@ -1,12 +1,12 @@ [ { - "version": "12", "path": "app/main.py", "language": "python", + "version": "13", "path": "app/main.py", "language": "python", "imports": [{"line": 1, "module": "shared", "alias": "shared", "kind": "static", "namespace": "shared"}], "definitions": [{"key": "app/main.py", "kind": "module", "name": "main", "start_line": 1, "end_line": 2, "exported": true}], "calls": [{"caller": "app/main.py", "line": 2, "column": 1, "callee": "shared.go", "targets": ["shared//go"], "receiver": "shared", "method": "go", "kind": "call"}] }, { - "version": "12", "path": "lib/shared.py", "language": "python", + "version": "13", "path": "lib/shared.py", "language": "python", "definitions": [ {"key": "lib/shared.py", "kind": "module", "name": "shared", "start_line": 1, "end_line": 2, "exported": true}, {"key": "lib/shared.py//go", "kind": "function", "name": "go", "start_line": 1, "end_line": 2, "exported": true} diff --git a/projectgraph/testdata/python/wildcard/graph.golden.json b/projectgraph/testdata/python/wildcard/graph.golden.json index 0ed0e59b..a725c3c9 100644 --- a/projectgraph/testdata/python/wildcard/graph.golden.json +++ b/projectgraph/testdata/python/wildcard/graph.golden.json @@ -112,7 +112,7 @@ "util.py": "indexed" }, "stats": { - "version": "12", + "version": "13", "files": 2, "nodes": 4, "edges": 3, diff --git a/projectgraph/testdata/python/wildcard/indexes.json b/projectgraph/testdata/python/wildcard/indexes.json index 46228204..2d1be2df 100644 --- a/projectgraph/testdata/python/wildcard/indexes.json +++ b/projectgraph/testdata/python/wildcard/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "12", "path": "main.py", "language": "python", + "version": "13", "path": "main.py", "language": "python", "imports": [{"line": 1, "module": "util", "wildcard": true, "kind": "static", "namespace": "util"}], "definitions": [{"key": "main.py", "kind": "module", "name": "main", "start_line": 1, "end_line": 3, "exported": true}], "calls": [ @@ -9,7 +9,7 @@ ] }, { - "version": "12", "path": "util.py", "language": "python", + "version": "13", "path": "util.py", "language": "python", "definitions": [ {"key": "util.py", "kind": "module", "name": "util", "start_line": 1, "end_line": 4, "exported": true}, {"key": "util.py//helper", "kind": "function", "name": "helper", "start_line": 1, "end_line": 2, "exported": true}, diff --git a/projectgraph/testdata/roots/extra-rules/graph.golden.json b/projectgraph/testdata/roots/extra-rules/graph.golden.json index 8b0e401c..63a0afe1 100644 --- a/projectgraph/testdata/roots/extra-rules/graph.golden.json +++ b/projectgraph/testdata/roots/extra-rules/graph.golden.json @@ -105,7 +105,7 @@ "views.py": "indexed" }, "stats": { - "version": "12", + "version": "13", "files": 3, "nodes": 7, "edges": 0, diff --git a/projectgraph/testdata/roots/extra-rules/indexes.json b/projectgraph/testdata/roots/extra-rules/indexes.json index 76047465..b6c142a8 100644 --- a/projectgraph/testdata/roots/extra-rules/indexes.json +++ b/projectgraph/testdata/roots/extra-rules/indexes.json @@ -1,10 +1,10 @@ [ { - "version": "12", "path": "app/users/route.ts", "language": "typescript", + "version": "13", "path": "app/users/route.ts", "language": "typescript", "definitions": [{"key": "app/users/route.ts", "kind": "module", "name": "route", "start_line": 1, "end_line": 1, "exported": true}] }, { - "version": "12", "path": "src/handler.js", "language": "javascript", + "version": "13", "path": "src/handler.js", "language": "javascript", "definitions": [ {"key": "src/handler.js", "kind": "module", "name": "handler", "start_line": 1, "end_line": 4, "exported": true}, {"key": "src/handler.js//Handler", "kind": "class", "name": "Handler", "start_line": 1, "end_line": 4, "exported": true}, @@ -13,7 +13,7 @@ ] }, { - "version": "12", "path": "views.py", "language": "python", + "version": "13", "path": "views.py", "language": "python", "definitions": [ {"key": "views.py", "kind": "module", "name": "views", "start_line": 1, "end_line": 3, "exported": true}, {"key": "views.py//index", "kind": "function", "name": "index", "start_line": 2, "end_line": 3, "exported": true, "decorators": ["@app.route"]} diff --git a/projectgraph/testdata/roots/library-surface/graph.golden.json b/projectgraph/testdata/roots/library-surface/graph.golden.json index 6376440b..1f691c4e 100644 --- a/projectgraph/testdata/roots/library-surface/graph.golden.json +++ b/projectgraph/testdata/roots/library-surface/graph.golden.json @@ -82,7 +82,7 @@ "lib/test_api.py": "indexed" }, "stats": { - "version": "12", + "version": "13", "files": 2, "nodes": 5, "edges": 0, diff --git a/projectgraph/testdata/roots/library-surface/indexes.json b/projectgraph/testdata/roots/library-surface/indexes.json index 670c5abd..16451349 100644 --- a/projectgraph/testdata/roots/library-surface/indexes.json +++ b/projectgraph/testdata/roots/library-surface/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "12", "path": "lib/api.py", "language": "python", + "version": "13", "path": "lib/api.py", "language": "python", "definitions": [ {"key": "lib/api.py", "kind": "module", "name": "api", "start_line": 1, "end_line": 4, "exported": true}, {"key": "lib/api.py//public", "kind": "function", "name": "public", "start_line": 1, "end_line": 2, "exported": true}, @@ -8,7 +8,7 @@ ] }, { - "version": "12", "path": "lib/test_api.py", "language": "python", + "version": "13", "path": "lib/test_api.py", "language": "python", "definitions": [ {"key": "lib/test_api.py", "kind": "module", "name": "test_api", "start_line": 1, "end_line": 2, "exported": true}, {"key": "lib/test_api.py//test_public", "kind": "function", "name": "test_public", "start_line": 1, "end_line": 2, "exported": true} diff --git a/projectgraph/testdata/roots/main-guard/graph.golden.json b/projectgraph/testdata/roots/main-guard/graph.golden.json index f4216cab..93ac6b0b 100644 --- a/projectgraph/testdata/roots/main-guard/graph.golden.json +++ b/projectgraph/testdata/roots/main-guard/graph.golden.json @@ -60,7 +60,7 @@ "lib.py": "indexed" }, "stats": { - "version": "12", + "version": "13", "files": 2, "nodes": 4, "edges": 1, diff --git a/projectgraph/testdata/roots/main-guard/indexes.json b/projectgraph/testdata/roots/main-guard/indexes.json index 71973966..c476e4a9 100644 --- a/projectgraph/testdata/roots/main-guard/indexes.json +++ b/projectgraph/testdata/roots/main-guard/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "12", "path": "app.py", "language": "python", "main_guard": true, + "version": "13", "path": "app.py", "language": "python", "main_guard": true, "definitions": [ {"key": "app.py", "kind": "module", "name": "app", "start_line": 1, "end_line": 5, "exported": true}, {"key": "app.py//run", "kind": "function", "name": "run", "start_line": 1, "end_line": 2, "exported": true} @@ -8,7 +8,7 @@ "calls": [{"caller": "app.py", "line": 5, "column": 5, "callee": "run", "targets": ["app.py//run"], "kind": "call"}] }, { - "version": "12", "path": "lib.py", "language": "python", + "version": "13", "path": "lib.py", "language": "python", "definitions": [ {"key": "lib.py", "kind": "module", "name": "lib", "start_line": 1, "end_line": 2, "exported": true}, {"key": "lib.py//lib", "kind": "function", "name": "lib", "start_line": 1, "end_line": 2, "exported": true} diff --git a/projectgraph/testdata/roots/package-json-bin/graph.golden.json b/projectgraph/testdata/roots/package-json-bin/graph.golden.json index d3333993..fb02953b 100644 --- a/projectgraph/testdata/roots/package-json-bin/graph.golden.json +++ b/projectgraph/testdata/roots/package-json-bin/graph.golden.json @@ -64,7 +64,7 @@ "lib/run.js": "indexed" }, "stats": { - "version": "12", + "version": "13", "files": 2, "nodes": 3, "edges": 2, diff --git a/projectgraph/testdata/roots/package-json-bin/indexes.json b/projectgraph/testdata/roots/package-json-bin/indexes.json index 744ee0b9..fdf03e94 100644 --- a/projectgraph/testdata/roots/package-json-bin/indexes.json +++ b/projectgraph/testdata/roots/package-json-bin/indexes.json @@ -1,12 +1,12 @@ [ { - "version": "12", "path": "bin/cli.js", "language": "javascript", + "version": "13", "path": "bin/cli.js", "language": "javascript", "imports": [{"line": 1, "module": "../lib/run", "item": "run", "alias": "run", "kind": "static", "namespace": "..//lib//run//run"}], "definitions": [{"key": "bin/cli.js", "kind": "module", "name": "cli", "start_line": 1, "end_line": 2, "exported": true}], "calls": [{"caller": "bin/cli.js", "line": 2, "column": 1, "callee": "run", "targets": ["..//lib//run//run"], "kind": "call"}] }, { - "version": "12", "path": "lib/run.js", "language": "javascript", + "version": "13", "path": "lib/run.js", "language": "javascript", "exports": [{"name": "run", "target": "lib/run.js//run", "line": 1}], "definitions": [ {"key": "lib/run.js", "kind": "module", "name": "run", "start_line": 1, "end_line": 1, "exported": true}, diff --git a/projectgraph/testdata/roots/package-json-main/graph.golden.json b/projectgraph/testdata/roots/package-json-main/graph.golden.json index dee2626d..4775ede5 100644 --- a/projectgraph/testdata/roots/package-json-main/graph.golden.json +++ b/projectgraph/testdata/roots/package-json-main/graph.golden.json @@ -154,7 +154,7 @@ "parser.js": "indexed" }, "stats": { - "version": "12", + "version": "13", "files": 4, "nodes": 9, "edges": 3, diff --git a/projectgraph/testdata/roots/package-json-main/indexes.json b/projectgraph/testdata/roots/package-json-main/indexes.json index 4077565c..af5612eb 100644 --- a/projectgraph/testdata/roots/package-json-main/indexes.json +++ b/projectgraph/testdata/roots/package-json-main/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "12", + "version": "13", "path": "cli.js", "language": "javascript", "exports": [ @@ -30,7 +30,7 @@ ] }, { - "version": "12", + "version": "13", "path": "fmt.js", "language": "javascript", "exports": [ @@ -60,7 +60,7 @@ ] }, { - "version": "12", + "version": "13", "path": "index.js", "language": "javascript", "exports": [ @@ -104,7 +104,7 @@ ] }, { - "version": "12", + "version": "13", "path": "parser.js", "language": "javascript", "exports": [ diff --git a/projectgraph/testdata/roots/procfile-dockerfile/graph.golden.json b/projectgraph/testdata/roots/procfile-dockerfile/graph.golden.json index ca146394..2c82a473 100644 --- a/projectgraph/testdata/roots/procfile-dockerfile/graph.golden.json +++ b/projectgraph/testdata/roots/procfile-dockerfile/graph.golden.json @@ -108,7 +108,7 @@ "worker.py": "indexed" }, "stats": { - "version": "12", + "version": "13", "files": 6, "nodes": 7, "edges": 0, diff --git a/projectgraph/testdata/roots/procfile-dockerfile/indexes.json b/projectgraph/testdata/roots/procfile-dockerfile/indexes.json index 96452321..4cb4c178 100644 --- a/projectgraph/testdata/roots/procfile-dockerfile/indexes.json +++ b/projectgraph/testdata/roots/procfile-dockerfile/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "12", + "version": "13", "path": "app/wsgi.py", "language": "python", "definitions": [ @@ -23,7 +23,7 @@ ] }, { - "version": "12", + "version": "13", "path": "app/cli.py", "language": "python", "definitions": [ @@ -38,7 +38,7 @@ ] }, { - "version": "12", + "version": "13", "path": "app/__init__.py", "language": "python", "definitions": [ @@ -53,7 +53,7 @@ ] }, { - "version": "12", + "version": "13", "path": "worker.py", "language": "python", "definitions": [ @@ -68,7 +68,7 @@ ] }, { - "version": "12", + "version": "13", "path": "ignored.py", "language": "python", "definitions": [ @@ -83,7 +83,7 @@ ] }, { - "version": "12", + "version": "13", "path": "web/server.js", "language": "javascript", "definitions": [ diff --git a/projectgraph/testdata/roots/pyproject-scripts/graph.golden.json b/projectgraph/testdata/roots/pyproject-scripts/graph.golden.json index b52db490..1b306710 100644 --- a/projectgraph/testdata/roots/pyproject-scripts/graph.golden.json +++ b/projectgraph/testdata/roots/pyproject-scripts/graph.golden.json @@ -146,7 +146,7 @@ "src/app/tool.py": "indexed" }, "stats": { - "version": "12", + "version": "13", "files": 5, "nodes": 10, "edges": 0, diff --git a/projectgraph/testdata/roots/pyproject-scripts/indexes.json b/projectgraph/testdata/roots/pyproject-scripts/indexes.json index 35c50143..12ea4146 100644 --- a/projectgraph/testdata/roots/pyproject-scripts/indexes.json +++ b/projectgraph/testdata/roots/pyproject-scripts/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "12", + "version": "13", "path": "src/app/cli.py", "language": "python", "definitions": [ @@ -23,7 +23,7 @@ ] }, { - "version": "12", + "version": "13", "path": "src/app/tool.py", "language": "python", "definitions": [ @@ -46,7 +46,7 @@ ] }, { - "version": "12", + "version": "13", "path": "src/app/plugin.py", "language": "python", "definitions": [ @@ -78,7 +78,7 @@ ] }, { - "version": "12", + "version": "13", "path": "src/app/legacy.py", "language": "python", "definitions": [ @@ -93,7 +93,7 @@ ] }, { - "version": "12", + "version": "13", "path": "src/app/ignored.py", "language": "python", "definitions": [ diff --git a/projectgraph/testdata/roots/setup-py/graph.golden.json b/projectgraph/testdata/roots/setup-py/graph.golden.json index 6b438460..8d9fe88b 100644 --- a/projectgraph/testdata/roots/setup-py/graph.golden.json +++ b/projectgraph/testdata/roots/setup-py/graph.golden.json @@ -74,7 +74,7 @@ "tool/main.py": "indexed" }, "stats": { - "version": "12", + "version": "13", "files": 2, "nodes": 4, "edges": 0, diff --git a/projectgraph/testdata/roots/setup-py/indexes.json b/projectgraph/testdata/roots/setup-py/indexes.json index 9caf273e..9423486b 100644 --- a/projectgraph/testdata/roots/setup-py/indexes.json +++ b/projectgraph/testdata/roots/setup-py/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "12", + "version": "13", "path": "tool/main.py", "language": "python", "definitions": [ @@ -23,7 +23,7 @@ ] }, { - "version": "12", + "version": "13", "path": "tool/admin.py", "language": "python", "definitions": [ diff --git a/projectgraph/testdata/roots/test-file/graph.golden.json b/projectgraph/testdata/roots/test-file/graph.golden.json index 3b77119d..01bcc76c 100644 --- a/projectgraph/testdata/roots/test-file/graph.golden.json +++ b/projectgraph/testdata/roots/test-file/graph.golden.json @@ -68,7 +68,7 @@ "tests/test_app.py": "indexed" }, "stats": { - "version": "12", + "version": "13", "files": 1, "nodes": 4, "edges": 0, diff --git a/projectgraph/testdata/roots/test-file/indexes.json b/projectgraph/testdata/roots/test-file/indexes.json index 315b5d8e..9164dd6b 100644 --- a/projectgraph/testdata/roots/test-file/indexes.json +++ b/projectgraph/testdata/roots/test-file/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "12", "path": "tests/test_app.py", "language": "python", + "version": "13", "path": "tests/test_app.py", "language": "python", "definitions": [ {"key": "tests/test_app.py", "kind": "module", "name": "test_app", "start_line": 1, "end_line": 6, "exported": true}, {"key": "tests/test_app.py//TestApp", "kind": "class", "name": "TestApp", "start_line": 1, "end_line": 4, "exported": true}, diff --git a/projectgraph/testdata/typescript/js-specifier/graph.golden.json b/projectgraph/testdata/typescript/js-specifier/graph.golden.json index d6289607..39236b74 100644 --- a/projectgraph/testdata/typescript/js-specifier/graph.golden.json +++ b/projectgraph/testdata/typescript/js-specifier/graph.golden.json @@ -82,7 +82,7 @@ "src/util.ts": "indexed" }, "stats": { - "version": "12", + "version": "13", "files": 2, "nodes": 3, "edges": 2, diff --git a/projectgraph/testdata/typescript/js-specifier/indexes.json b/projectgraph/testdata/typescript/js-specifier/indexes.json index 8d4c4613..8139e260 100644 --- a/projectgraph/testdata/typescript/js-specifier/indexes.json +++ b/projectgraph/testdata/typescript/js-specifier/indexes.json @@ -1,12 +1,12 @@ [ { - "version": "12", "path": "src/main.ts", "language": "typescript", + "version": "13", "path": "src/main.ts", "language": "typescript", "imports": [{"line": 1, "module": "./util.js", "item": "f", "alias": "f", "kind": "static", "namespace": ".//util.js//f"}], "definitions": [{"key": "src/main.ts", "kind": "module", "name": "main", "start_line": 1, "end_line": 2, "exported": true}], "calls": [{"caller": "src/main.ts", "line": 2, "column": 1, "callee": "f", "targets": [".//util.js//f"], "kind": "call"}] }, { - "version": "12", "path": "src/util.ts", "language": "typescript", + "version": "13", "path": "src/util.ts", "language": "typescript", "exports": [{"name": "f", "target": "src/util.ts//f", "line": 1}], "definitions": [ {"key": "src/util.ts", "kind": "module", "name": "util", "start_line": 1, "end_line": 1, "exported": true}, diff --git a/projectgraph/testdata/typescript/tsconfig-paths/graph.golden.json b/projectgraph/testdata/typescript/tsconfig-paths/graph.golden.json index 109ccf31..155930a9 100644 --- a/projectgraph/testdata/typescript/tsconfig-paths/graph.golden.json +++ b/projectgraph/testdata/typescript/tsconfig-paths/graph.golden.json @@ -138,7 +138,7 @@ "src/main.ts": "indexed" }, "stats": { - "version": "12", + "version": "13", "files": 3, "nodes": 5, "edges": 4, diff --git a/projectgraph/testdata/typescript/tsconfig-paths/indexes.json b/projectgraph/testdata/typescript/tsconfig-paths/indexes.json index 90dedc99..f88701f1 100644 --- a/projectgraph/testdata/typescript/tsconfig-paths/indexes.json +++ b/projectgraph/testdata/typescript/tsconfig-paths/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "12", "path": "src/main.ts", "language": "typescript", + "version": "13", "path": "src/main.ts", "language": "typescript", "imports": [ {"line": 1, "module": "@app/server", "item": "serve", "alias": "serve", "kind": "static", "namespace": "@app//server//serve"}, {"line": 2, "module": "src/config", "item": "cfg", "alias": "cfg", "kind": "static", "namespace": "src//config//cfg"} @@ -12,7 +12,7 @@ ] }, { - "version": "12", "path": "src/app/server.ts", "language": "typescript", + "version": "13", "path": "src/app/server.ts", "language": "typescript", "exports": [{"name": "serve", "target": "src/app/server.ts//serve", "line": 1}], "definitions": [ {"key": "src/app/server.ts", "kind": "module", "name": "server", "start_line": 1, "end_line": 1, "exported": true}, @@ -20,7 +20,7 @@ ] }, { - "version": "12", "path": "src/config.ts", "language": "typescript", + "version": "13", "path": "src/config.ts", "language": "typescript", "exports": [{"name": "cfg", "target": "src/config.ts//cfg", "line": 1}], "definitions": [ {"key": "src/config.ts", "kind": "module", "name": "config", "start_line": 1, "end_line": 1, "exported": true}, diff --git a/projectgraph/testdata/typescript/type-only/graph.golden.json b/projectgraph/testdata/typescript/type-only/graph.golden.json index b7e756ff..9b634a6a 100644 --- a/projectgraph/testdata/typescript/type-only/graph.golden.json +++ b/projectgraph/testdata/typescript/type-only/graph.golden.json @@ -97,7 +97,7 @@ "src/types.ts": "indexed" }, "stats": { - "version": "12", + "version": "13", "files": 3, "nodes": 5, "edges": 2, diff --git a/projectgraph/testdata/typescript/type-only/indexes.json b/projectgraph/testdata/typescript/type-only/indexes.json index 2da7f73f..2adfa519 100644 --- a/projectgraph/testdata/typescript/type-only/indexes.json +++ b/projectgraph/testdata/typescript/type-only/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "12", "path": "src/main.ts", "language": "typescript", + "version": "13", "path": "src/main.ts", "language": "typescript", "imports": [ {"line": 1, "module": "./types", "item": "Opts", "alias": "Opts", "kind": "type-only", "namespace": ".//types//Opts"}, {"line": 2, "module": "./decl", "item": "decl", "alias": "decl", "kind": "static", "namespace": ".//decl//decl"} @@ -9,7 +9,7 @@ "calls": [{"caller": "src/main.ts", "line": 3, "column": 1, "callee": "decl", "targets": [".//decl//decl"], "kind": "call"}] }, { - "version": "12", "path": "src/types.ts", "language": "typescript", + "version": "13", "path": "src/types.ts", "language": "typescript", "exports": [{"name": "Opts", "target": "src/types.ts//Opts", "line": 1}], "definitions": [ {"key": "src/types.ts", "kind": "module", "name": "types", "start_line": 1, "end_line": 1, "exported": true}, @@ -17,7 +17,7 @@ ] }, { - "version": "12", "path": "src/decl.d.ts", "language": "typescript", + "version": "13", "path": "src/decl.d.ts", "language": "typescript", "exports": [{"name": "decl", "target": "src/decl.d.ts//decl", "line": 1}], "definitions": [ {"key": "src/decl.d.ts", "kind": "module", "name": "decl", "start_line": 1, "end_line": 1, "exported": true}, diff --git a/projectgraph/walk.go b/projectgraph/walk.go index 12191627..7e504d4b 100644 --- a/projectgraph/walk.go +++ b/projectgraph/walk.go @@ -65,6 +65,16 @@ func (b *builder) walkFrom(r resolution, names []string) resolution { case kind == NodeClass || kind == NodeInterface: consumed, done = b.walkClass(&r, name, i+1 == len(names), &instance) case kind == NodeExternal || kind == "" && b.g.IsExternal(r.key): + // A name before the last one under an external key is a call + // of a function or a method whose result has the next name, + // when a unit of dependency code gives the function. + if i+1 < len(names) { + if class, ok := b.externalResult(r.key, name); ok { + r.key, r.virtual, r.declared = class, true, true + i++ + continue + } + } r.key = b.deriveExternal(r.key, names[i:]) r.rule = cmp.Or(r.rule, ruleExternal) return r @@ -220,11 +230,12 @@ func (b *builder) walkClass(r *resolution, name string, last bool, instance *boo // variable or a parameter of the function, and the index does not know its // value. func (b *builder) walkFunction(r *resolution, name string) (consumed, done bool) { - if class, ok := b.resultType(r.key); ok { + class, reason, ok := b.resultType(fileindex.PackageOf(r.key), r.key, 0) + if ok { r.key, r.virtual, r.declared = class, true, true return false, false } - r.fail = &failure{reason: fileindex.UnresolvedUnknownReceiver, name: name} + r.fail = &failure{reason: reason, name: name} return false, true } diff --git a/test/projectgraph/cases/go/chained-external-call/expect.yaml b/test/projectgraph/cases/go/chained-external-call/expect.yaml index 34bc0250..dac25974 100644 --- a/test/projectgraph/cases/go/chained-external-call/expect.yaml +++ b/test/projectgraph/cases/go/chained-external-call/expect.yaml @@ -1,7 +1,8 @@ source: safedep:hand description: >- run calls Next on the result of html.NewTokenizer, with no dependency code. The link step does not know the - type of that result, so the site keeps a record with the method Next and the reason unknown-receiver. The + type of that result, so the site keeps a record with the method Next. No unit gives the package, so the + reason is no-dependency-source. The target Tokenizer.Next is then uncertain at the site, not no-such-target. The edge to the key html//NewTokenizer//Next stays, because a Go call of a type name converts a value, and a method of the type then runs. @@ -40,7 +41,7 @@ edges: unresolved: - caller: tokens.go//run site: tokens.go:10 - reason: unknown-receiver + reason: no-dependency-source method: Next results: - target: golang.org//x//net//html//Tokenizer//Next diff --git a/test/projectgraph/cases/go/dependency-build-files/summaries/go_github.com_acme_kit_v1.0.0.json b/test/projectgraph/cases/go/dependency-build-files/summaries/go_github.com_acme_kit_v1.0.0.json index 15b1ed48..129306a5 100644 --- a/test/projectgraph/cases/go/dependency-build-files/summaries/go_github.com_acme_kit_v1.0.0.json +++ b/test/projectgraph/cases/go/dependency-build-files/summaries/go_github.com_acme_kit_v1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", - "index_version": "12", - "link_version": "12", + "index_version": "13", + "link_version": "13", "unit": "go:github.com/acme/kit@v1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/go/dependency-generated-code/summaries/go_github.com_acme_kit_v1.0.0.json b/test/projectgraph/cases/go/dependency-generated-code/summaries/go_github.com_acme_kit_v1.0.0.json index 5ec9339d..fb6a75d2 100644 --- a/test/projectgraph/cases/go/dependency-generated-code/summaries/go_github.com_acme_kit_v1.0.0.json +++ b/test/projectgraph/cases/go/dependency-generated-code/summaries/go_github.com_acme_kit_v1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", - "index_version": "12", - "link_version": "12", + "index_version": "13", + "link_version": "13", "unit": "go:github.com/acme/kit@v1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/go/dependency-implements-interface/expect.yaml b/test/projectgraph/cases/go/dependency-implements-interface/expect.yaml index 78d245e8..253cdef4 100644 --- a/test/projectgraph/cases/go/dependency-implements-interface/expect.yaml +++ b/test/projectgraph/cases/go/dependency-implements-interface/expect.yaml @@ -2,7 +2,9 @@ source: safedep:hand description: >- main holds a value of the project interface Runner and calls Run on it. The value comes from New of the dependency dep, whose type Impl implements Runner and calls exec.Command in Run. The interface is - first-party, so Impl.Run of the unit is a cha candidate, and the site keeps its record. The record has + first-party, so Impl.Run of the unit is a cha candidate, and the site keeps its record. The cha edge goes to + the external key of Impl.Run, so a target of that key finds the path, and a package edge joins the key to + the definition. The record has the method Run, and Impl.Run reaches exec.Command, so the target is never not found. units: gomod/github.com/acme/dep@v1.0.0: go:github.com/acme/dep@v1.0.0 @@ -26,10 +28,15 @@ edges: confidence: high site: go:github.com/acme/dep@v1.0.0//dep.go:7 - from: main.go//main - to: go:github.com/acme/dep@v1.0.0//dep.go//Impl//Run + to: github.com//acme//dep//Impl//Run kind: cha confidence: medium site: main.go:10 + - from: github.com//acme//dep//Impl//Run + to: go:github.com/acme/dep@v1.0.0//dep.go//Impl//Run + kind: package + confidence: high + site: go:github.com/acme/dep@v1.0.0//dep.go:11 - from: go:github.com/acme/dep@v1.0.0//dep.go//Impl//Run to: os//exec//Command kind: direct @@ -45,10 +52,14 @@ results: status: found path: - key: main.go//main - - key: go:github.com/acme/dep@v1.0.0//dep.go//Impl//Run + - key: github.com//acme//dep//Impl//Run site: main.go:10 kind: cha confidence: medium + - key: go:github.com/acme/dep@v1.0.0//dep.go//Impl//Run + site: go:github.com/acme/dep@v1.0.0//dep.go:11 + kind: package + confidence: high - key: os//exec//Command site: go:github.com/acme/dep@v1.0.0//dep.go:12 kind: direct @@ -57,3 +68,15 @@ results: - caller: go:github.com/acme/dep@v1.0.0//dep.go//Impl//Run site: go:github.com/acme/dep@v1.0.0//dep.go:12 reached: true + - target: github.com//acme//dep//Impl//Run + status: found + path: + - key: main.go//main + - key: github.com//acme//dep//Impl//Run + site: main.go:10 + kind: cha + confidence: medium + call_sites: + - caller: main.go//main + site: main.go:10 + reached: true diff --git a/test/projectgraph/cases/go/dependency-implements-interface/summaries/go_github.com_acme_dep_v1.0.0.json b/test/projectgraph/cases/go/dependency-implements-interface/summaries/go_github.com_acme_dep_v1.0.0.json index 00e7c63f..530e12a3 100644 --- a/test/projectgraph/cases/go/dependency-implements-interface/summaries/go_github.com_acme_dep_v1.0.0.json +++ b/test/projectgraph/cases/go/dependency-implements-interface/summaries/go_github.com_acme_dep_v1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", - "index_version": "12", - "link_version": "12", + "index_version": "13", + "link_version": "13", "unit": "go:github.com/acme/dep@v1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/go/dependency-module/summaries/go_github.com_acme_yamlish_v1.2.0.json b/test/projectgraph/cases/go/dependency-module/summaries/go_github.com_acme_yamlish_v1.2.0.json index f4f1cdd7..7a343e6f 100644 --- a/test/projectgraph/cases/go/dependency-module/summaries/go_github.com_acme_yamlish_v1.2.0.json +++ b/test/projectgraph/cases/go/dependency-module/summaries/go_github.com_acme_yamlish_v1.2.0.json @@ -1,7 +1,7 @@ { "version": "2", - "index_version": "12", - "link_version": "12", + "index_version": "13", + "link_version": "13", "unit": "go:github.com/acme/yamlish@v1.2.0", "symbols": [ { diff --git a/test/projectgraph/cases/go/dependency-platform-files/summaries/go_github.com_acme_kit_v1.0.0.json b/test/projectgraph/cases/go/dependency-platform-files/summaries/go_github.com_acme_kit_v1.0.0.json index 5ec9339d..fb6a75d2 100644 --- a/test/projectgraph/cases/go/dependency-platform-files/summaries/go_github.com_acme_kit_v1.0.0.json +++ b/test/projectgraph/cases/go/dependency-platform-files/summaries/go_github.com_acme_kit_v1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", - "index_version": "12", - "link_version": "12", + "index_version": "13", + "link_version": "13", "unit": "go:github.com/acme/kit@v1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/go/dependency-result-types/expect.yaml b/test/projectgraph/cases/go/dependency-result-types/expect.yaml new file mode 100644 index 00000000..f81aaaeb --- /dev/null +++ b/test/projectgraph/cases/go/dependency-result-types/expect.yaml @@ -0,0 +1,218 @@ +source: safedep:hand +description: >- + tokens holds z := markup.NewTokenizer(nil) and calls z.Next, and calls Close on the result of Raw on the result + of NewTokenizer. tree calls Render on the first result of Parse, and send calls Write on the first result of + http.NewRequest. The unit of markup gives the result type of each function and method, and the unit of the + standard library, as GOROOT/src gives it, gives the type of NewRequest. So each call gets the external key of + the method of the type, as example.com//markup//Tokenizer//Next, and a package edge joins the key to the + definition of the unit. +units: + gomod/example.com/markup@v1.0.0: go:example.com/markup@v1.0.0 + goroot/src: go:std@go1.25.1 +roots: + - key: main.go + kind: main + rule: go.main + - key: main.go//main + kind: main + rule: go.main +exact_edges: true +edges: + - from: example.com//markup//NewTokenizer + to: go:example.com/markup@v1.0.0//token.go//NewTokenizer + kind: package + confidence: high + site: go:example.com/markup@v1.0.0//token.go:14 + - from: example.com//markup//Node//Render + to: go:example.com/markup@v1.0.0//node.go//Node//Render + kind: package + confidence: high + site: go:example.com/markup@v1.0.0//node.go:14 + - from: example.com//markup//Parse + to: go:example.com/markup@v1.0.0//node.go//Parse + kind: package + confidence: high + site: go:example.com/markup@v1.0.0//node.go:10 + - from: example.com//markup//Text//Close + to: go:example.com/markup@v1.0.0//token.go//Text//Close + kind: package + confidence: high + site: go:example.com/markup@v1.0.0//token.go:27 + - from: example.com//markup//Tokenizer//Next + to: go:example.com/markup@v1.0.0//token.go//Tokenizer//Next + kind: package + confidence: high + site: go:example.com/markup@v1.0.0//token.go:18 + - from: example.com//markup//Tokenizer//Raw + to: go:example.com/markup@v1.0.0//token.go//Tokenizer//Raw + kind: package + confidence: high + site: go:example.com/markup@v1.0.0//token.go:23 + - from: go:example.com/markup@v1.0.0//node.go//Node//Render + to: os//Remove + kind: direct + confidence: high + site: go:example.com/markup@v1.0.0//node.go:15 + - from: go:example.com/markup@v1.0.0//token.go//Text//Close + to: os//exec//Command + kind: direct + confidence: high + site: go:example.com/markup@v1.0.0//token.go:28 + - from: go:example.com/markup@v1.0.0//token.go//Tokenizer//Next + to: os//exec//Command + kind: direct + confidence: high + site: go:example.com/markup@v1.0.0//token.go:19 + - from: go:std@go1.25.1//net/http/request.go//Request//Write + to: go:std@go1.25.1//os/file.go//Getenv + kind: direct + confidence: high + site: go:std@go1.25.1//net/http/request.go:15 + - from: main.go//main + to: main.go//tokens + kind: direct + confidence: high + site: main.go:10 + - from: main.go//main + to: main.go//tree + kind: direct + confidence: high + site: main.go:11 + - from: main.go//main + to: main.go//send + kind: direct + confidence: high + site: main.go:12 + - from: main.go//send + to: net//http//NewRequest + kind: direct + confidence: high + site: main.go:30 + - from: main.go//send + to: net//http//Request//Write + kind: direct + confidence: high + site: main.go:31 + - from: main.go//tokens + to: example.com//markup//NewTokenizer + kind: direct + confidence: high + site: main.go:16 + - from: main.go//tokens + to: example.com//markup//Tokenizer//Next + kind: direct + confidence: high + site: main.go:17 + - from: main.go//tokens + to: example.com//markup//NewTokenizer + kind: direct + confidence: high + site: main.go:18 + - from: main.go//tokens + to: example.com//markup//Text//Close + kind: direct + confidence: high + site: main.go:18 + - from: main.go//tokens + to: example.com//markup//Tokenizer//Raw + kind: direct + confidence: high + site: main.go:18 + - from: main.go//tree + to: example.com//markup//Parse + kind: direct + confidence: high + site: main.go:22 + - from: main.go//tree + to: example.com//markup//Node//Render + kind: direct + confidence: high + site: main.go:26 + - from: net//http//NewRequest + to: go:std@go1.25.1//net/http/request.go//NewRequest + kind: package + confidence: high + site: go:std@go1.25.1//net/http/request.go:10 + - from: net//http//Request//Write + to: go:std@go1.25.1//net/http/request.go//Request//Write + kind: package + confidence: high + site: go:std@go1.25.1//net/http/request.go:14 + - from: os//Remove + to: go:std@go1.25.1//os/file.go//Remove + kind: package + confidence: high + site: go:std@go1.25.1//os/file.go:7 + - from: os//exec//Command + to: go:std@go1.25.1//os/exec/exec.go//Command + kind: package + confidence: high + site: go:std@go1.25.1//os/exec/exec.go:5 +unresolved: [] +results: + - target: example.com//markup//Tokenizer//Next + status: found + path: + - key: main.go//main + - key: main.go//tokens + site: main.go:10 + kind: direct + confidence: high + - key: example.com//markup//Tokenizer//Next + site: main.go:17 + kind: direct + confidence: high + call_sites: + - caller: main.go//tokens + site: main.go:17 + reached: true + - target: example.com//markup//Text//Close + status: found + path: + - key: main.go//main + - key: main.go//tokens + site: main.go:10 + kind: direct + confidence: high + - key: example.com//markup//Text//Close + site: main.go:18 + kind: direct + confidence: high + call_sites: + - caller: main.go//tokens + site: main.go:18 + reached: true + - target: example.com//markup//Node//Render + status: found + path: + - key: main.go//main + - key: main.go//tree + site: main.go:11 + kind: direct + confidence: high + - key: example.com//markup//Node//Render + site: main.go:26 + kind: direct + confidence: high + call_sites: + - caller: main.go//tree + site: main.go:26 + reached: true + - target: net//http//Request//Write + status: found + path: + - key: main.go//main + - key: main.go//send + site: main.go:12 + kind: direct + confidence: high + - key: net//http//Request//Write + site: main.go:31 + kind: direct + confidence: high + call_sites: + - caller: main.go//send + site: main.go:31 + reached: true + - target: example.com//markup//NewTokenizer//Next + status: no-such-target diff --git a/test/projectgraph/cases/go/dependency-result-types/project/go.mod b/test/projectgraph/cases/go/dependency-result-types/project/go.mod new file mode 100644 index 00000000..b4b143f8 --- /dev/null +++ b/test/projectgraph/cases/go/dependency-result-types/project/go.mod @@ -0,0 +1,5 @@ +module example.com/app + +go 1.22 + +require example.com/markup v1.0.0 diff --git a/test/projectgraph/cases/go/dependency-result-types/project/gomod/example.com/markup@v1.0.0/go.mod b/test/projectgraph/cases/go/dependency-result-types/project/gomod/example.com/markup@v1.0.0/go.mod new file mode 100644 index 00000000..2faaaa0c --- /dev/null +++ b/test/projectgraph/cases/go/dependency-result-types/project/gomod/example.com/markup@v1.0.0/go.mod @@ -0,0 +1,3 @@ +module example.com/markup + +go 1.22 diff --git a/test/projectgraph/cases/go/dependency-result-types/project/gomod/example.com/markup@v1.0.0/node.go b/test/projectgraph/cases/go/dependency-result-types/project/gomod/example.com/markup@v1.0.0/node.go new file mode 100644 index 00000000..2ec57a04 --- /dev/null +++ b/test/projectgraph/cases/go/dependency-result-types/project/gomod/example.com/markup@v1.0.0/node.go @@ -0,0 +1,16 @@ +package markup + +import ( + "io" + "os" +) + +type Node struct{} + +func Parse(r io.Reader) (*Node, error) { + return &Node{}, nil +} + +func (n *Node) Render(w io.Writer) { + os.Remove("render") +} diff --git a/test/projectgraph/cases/go/dependency-result-types/project/gomod/example.com/markup@v1.0.0/token.go b/test/projectgraph/cases/go/dependency-result-types/project/gomod/example.com/markup@v1.0.0/token.go new file mode 100644 index 00000000..6433c2c2 --- /dev/null +++ b/test/projectgraph/cases/go/dependency-result-types/project/gomod/example.com/markup@v1.0.0/token.go @@ -0,0 +1,29 @@ +package markup + +import ( + "io" + "os/exec" +) + +type Tokenizer struct { + r io.Reader +} + +type Text struct{} + +func NewTokenizer(r io.Reader) *Tokenizer { + return &Tokenizer{r: r} +} + +func (z *Tokenizer) Next() int { + exec.Command("next") + return 0 +} + +func (z *Tokenizer) Raw() *Text { + return &Text{} +} + +func (t *Text) Close() { + exec.Command("close") +} diff --git a/test/projectgraph/cases/go/dependency-result-types/project/goroot/src/go.mod b/test/projectgraph/cases/go/dependency-result-types/project/goroot/src/go.mod new file mode 100644 index 00000000..1ded0318 --- /dev/null +++ b/test/projectgraph/cases/go/dependency-result-types/project/goroot/src/go.mod @@ -0,0 +1,3 @@ +module std + +go 1.25 diff --git a/test/projectgraph/cases/go/dependency-result-types/project/goroot/src/io/io.go b/test/projectgraph/cases/go/dependency-result-types/project/goroot/src/io/io.go new file mode 100644 index 00000000..bb54e08e --- /dev/null +++ b/test/projectgraph/cases/go/dependency-result-types/project/goroot/src/io/io.go @@ -0,0 +1,9 @@ +package io + +type Reader interface { + Read(p []byte) (int, error) +} + +type Writer interface { + Write(p []byte) (int, error) +} diff --git a/test/projectgraph/cases/go/dependency-result-types/project/goroot/src/net/http/request.go b/test/projectgraph/cases/go/dependency-result-types/project/goroot/src/net/http/request.go new file mode 100644 index 00000000..8e9af287 --- /dev/null +++ b/test/projectgraph/cases/go/dependency-result-types/project/goroot/src/net/http/request.go @@ -0,0 +1,17 @@ +package http + +import ( + "io" + "os" +) + +type Request struct{} + +func NewRequest(method, url string, body io.Reader) (*Request, error) { + return &Request{}, nil +} + +func (r *Request) Write(w io.Writer) error { + os.Getenv("write") + return nil +} diff --git a/test/projectgraph/cases/go/dependency-result-types/project/goroot/src/os/exec/exec.go b/test/projectgraph/cases/go/dependency-result-types/project/goroot/src/os/exec/exec.go new file mode 100644 index 00000000..2e151876 --- /dev/null +++ b/test/projectgraph/cases/go/dependency-result-types/project/goroot/src/os/exec/exec.go @@ -0,0 +1,7 @@ +package exec + +type Cmd struct{} + +func Command(name string, arg ...string) *Cmd { + return &Cmd{} +} diff --git a/test/projectgraph/cases/go/dependency-result-types/project/goroot/src/os/file.go b/test/projectgraph/cases/go/dependency-result-types/project/goroot/src/os/file.go new file mode 100644 index 00000000..070605f5 --- /dev/null +++ b/test/projectgraph/cases/go/dependency-result-types/project/goroot/src/os/file.go @@ -0,0 +1,9 @@ +package os + +func Getenv(key string) string { + return "" +} + +func Remove(name string) error { + return nil +} diff --git a/test/projectgraph/cases/go/dependency-result-types/project/main.go b/test/projectgraph/cases/go/dependency-result-types/project/main.go new file mode 100644 index 00000000..bb6aa6fe --- /dev/null +++ b/test/projectgraph/cases/go/dependency-result-types/project/main.go @@ -0,0 +1,32 @@ +package main + +import ( + "net/http" + + "example.com/markup" +) + +func main() { + tokens() + tree() + send() +} + +func tokens() { + z := markup.NewTokenizer(nil) + z.Next() + markup.NewTokenizer(nil).Raw().Close() +} + +func tree() { + n, err := markup.Parse(nil) + if err != nil { + return + } + n.Render(nil) +} + +func send() { + req, _ := http.NewRequest("GET", "http://example.com", nil) + req.Write(nil) +} diff --git a/test/projectgraph/cases/go/dependency-result-types/summaries/go_example.com_markup_v1.0.0.json b/test/projectgraph/cases/go/dependency-result-types/summaries/go_example.com_markup_v1.0.0.json new file mode 100644 index 00000000..ae764cc0 --- /dev/null +++ b/test/projectgraph/cases/go/dependency-result-types/summaries/go_example.com_markup_v1.0.0.json @@ -0,0 +1,81 @@ +{ + "version": "2", + "index_version": "13", + "link_version": "13", + "unit": "go:example.com/markup@v1.0.0", + "symbols": [ + { + "key": "example.com//markup//NewTokenizer", + "definitions": [ + "go:example.com/markup@v1.0.0//token.go//NewTokenizer" + ], + "reaches": [] + }, + { + "key": "example.com//markup//Node", + "definitions": [ + "go:example.com/markup@v1.0.0//node.go//Node" + ], + "reaches": [] + }, + { + "key": "example.com//markup//Node//Render", + "definitions": [ + "go:example.com/markup@v1.0.0//node.go//Node//Render" + ], + "reaches": [ + "os//Remove" + ] + }, + { + "key": "example.com//markup//Parse", + "definitions": [ + "go:example.com/markup@v1.0.0//node.go//Parse" + ], + "reaches": [] + }, + { + "key": "example.com//markup//Text", + "definitions": [ + "go:example.com/markup@v1.0.0//token.go//Text" + ], + "reaches": [] + }, + { + "key": "example.com//markup//Text//Close", + "definitions": [ + "go:example.com/markup@v1.0.0//token.go//Text//Close" + ], + "reaches": [ + "os//exec//Command" + ] + }, + { + "key": "example.com//markup//Tokenizer", + "definitions": [ + "go:example.com/markup@v1.0.0//token.go//Tokenizer" + ], + "reaches": [] + }, + { + "key": "example.com//markup//Tokenizer//Next", + "definitions": [ + "go:example.com/markup@v1.0.0//token.go//Tokenizer//Next" + ], + "reaches": [ + "os//exec//Command" + ] + }, + { + "key": "example.com//markup//Tokenizer//Raw", + "definitions": [ + "go:example.com/markup@v1.0.0//token.go//Tokenizer//Raw" + ], + "reaches": [] + } + ], + "targets": [ + "os//Remove", + "os//exec//Command" + ] +} diff --git a/test/projectgraph/cases/go/dependency-result-types/summaries/go_std_go1.25.1.json b/test/projectgraph/cases/go/dependency-result-types/summaries/go_std_go1.25.1.json new file mode 100644 index 00000000..780d5871 --- /dev/null +++ b/test/projectgraph/cases/go/dependency-result-types/summaries/go_std_go1.25.1.json @@ -0,0 +1,72 @@ +{ + "version": "2", + "index_version": "13", + "link_version": "13", + "unit": "go:std@go1.25.1", + "symbols": [ + { + "key": "io//Reader", + "definitions": [ + "go:std@go1.25.1//io/io.go//Reader" + ], + "reaches": [] + }, + { + "key": "io//Writer", + "definitions": [ + "go:std@go1.25.1//io/io.go//Writer" + ], + "reaches": [] + }, + { + "key": "net//http//NewRequest", + "definitions": [ + "go:std@go1.25.1//net/http/request.go//NewRequest" + ], + "reaches": [] + }, + { + "key": "net//http//Request", + "definitions": [ + "go:std@go1.25.1//net/http/request.go//Request" + ], + "reaches": [] + }, + { + "key": "net//http//Request//Write", + "definitions": [ + "go:std@go1.25.1//net/http/request.go//Request//Write" + ], + "reaches": [] + }, + { + "key": "os//Getenv", + "definitions": [ + "go:std@go1.25.1//os/file.go//Getenv" + ], + "reaches": [] + }, + { + "key": "os//Remove", + "definitions": [ + "go:std@go1.25.1//os/file.go//Remove" + ], + "reaches": [] + }, + { + "key": "os//exec//Cmd", + "definitions": [ + "go:std@go1.25.1//os/exec/exec.go//Cmd" + ], + "reaches": [] + }, + { + "key": "os//exec//Command", + "definitions": [ + "go:std@go1.25.1//os/exec/exec.go//Command" + ], + "reaches": [] + } + ], + "targets": [] +} diff --git a/test/projectgraph/cases/go/interface-method-set/expect.yaml b/test/projectgraph/cases/go/interface-method-set/expect.yaml index 654456c1..0d27bdce 100644 --- a/test/projectgraph/cases/go/interface-method-set/expect.yaml +++ b/test/projectgraph/cases/go/interface-method-set/expect.yaml @@ -1,5 +1,5 @@ source: safedep:hand -description: start calls Run on a parameter of the interface type Runner, which declares Run and Stop. Real has both methods, so it can hold the call. Script has Run with the arity of the call but no Stop, so it is no Runner and its Run gets no cha edge. The site keeps a record with the method Run, and a definition named Run reaches exec.Command, so exec.Command is uncertain and not proved unreached. Script.Run calls Run on the result of exec.Command. The link step does not know the type of that result with no dependency code, so the site keeps a record. +description: start calls Run on a parameter of the interface type Runner, which declares Run and Stop. Real has both methods, so it can hold the call. Script has Run with the arity of the call but no Stop, so it is no Runner and its Run gets no cha edge. The site keeps a record with the method Run, and a definition named Run reaches exec.Command, so exec.Command is uncertain and not proved unreached. Script.Run calls Run on the result of exec.Command. No unit gives the code of os/exec, so the site keeps a record with the reason no-dependency-source. roots: - key: cmd/app/main.go kind: main @@ -23,7 +23,7 @@ unresolved: method: Run - caller: worker/script.go//Script//Run site: worker/script.go:8 - reason: unknown-receiver + reason: no-dependency-source method: Run results: - target: net//http//Get diff --git a/test/projectgraph/cases/javascript/commander-action/expect.yaml b/test/projectgraph/cases/javascript/commander-action/expect.yaml index 139f9e23..d3fc5b82 100644 --- a/test/projectgraph/cases/javascript/commander-action/expect.yaml +++ b/test/projectgraph/cases/javascript/commander-action/expect.yaml @@ -1,5 +1,5 @@ source: safedep:hand -description: program.command('convert ').action(convert) resolves to a key under commander, so convert is a root of the rule node.commander.action at the call. The call of action is a call on the result of command, whose type the link step does not know, so the site keeps a record. +description: program.command('convert ').action(convert) resolves to a key under commander, so convert is a root of the rule node.commander.action at the call. The call of action is a call on the result of command, whose type the link step does not know with no unit of commander, so the site keeps a record with the reason no-dependency-source. roots: - key: bin/cli.js//convert kind: framework @@ -14,7 +14,7 @@ edges: unresolved: - caller: bin/cli.js site: bin/cli.js:8 - reason: unknown-receiver + reason: no-dependency-source method: action results: - target: js-yaml//load diff --git a/test/projectgraph/cases/javascript/dependency-candidates/summaries/npm_store-kit_1.0.0.json b/test/projectgraph/cases/javascript/dependency-candidates/summaries/npm_store-kit_1.0.0.json index 2d0fc5dc..69390d5d 100644 --- a/test/projectgraph/cases/javascript/dependency-candidates/summaries/npm_store-kit_1.0.0.json +++ b/test/projectgraph/cases/javascript/dependency-candidates/summaries/npm_store-kit_1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", - "index_version": "12", - "link_version": "12", + "index_version": "13", + "link_version": "13", "unit": "npm:store-kit@1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/javascript/dependency-exports-import/summaries/npm_kit_1.0.0.json b/test/projectgraph/cases/javascript/dependency-exports-import/summaries/npm_kit_1.0.0.json index 21bdbc6a..32dad2c1 100644 --- a/test/projectgraph/cases/javascript/dependency-exports-import/summaries/npm_kit_1.0.0.json +++ b/test/projectgraph/cases/javascript/dependency-exports-import/summaries/npm_kit_1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", - "index_version": "12", - "link_version": "12", + "index_version": "13", + "link_version": "13", "unit": "npm:kit@1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/javascript/dependency-exports-require/summaries/npm_kit_1.0.0.json b/test/projectgraph/cases/javascript/dependency-exports-require/summaries/npm_kit_1.0.0.json index 21bdbc6a..32dad2c1 100644 --- a/test/projectgraph/cases/javascript/dependency-exports-require/summaries/npm_kit_1.0.0.json +++ b/test/projectgraph/cases/javascript/dependency-exports-require/summaries/npm_kit_1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", - "index_version": "12", - "link_version": "12", + "index_version": "13", + "link_version": "13", "unit": "npm:kit@1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/javascript/dependency-method-name/summaries/npm_plug_1.0.0.json b/test/projectgraph/cases/javascript/dependency-method-name/summaries/npm_plug_1.0.0.json index 49f531f9..7fb4d425 100644 --- a/test/projectgraph/cases/javascript/dependency-method-name/summaries/npm_plug_1.0.0.json +++ b/test/projectgraph/cases/javascript/dependency-method-name/summaries/npm_plug_1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", - "index_version": "12", - "link_version": "12", + "index_version": "13", + "link_version": "13", "unit": "npm:plug@1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/javascript/dependency-same-path/summaries/npm_util-kit_1.0.0.json b/test/projectgraph/cases/javascript/dependency-same-path/summaries/npm_util-kit_1.0.0.json index fd462e57..9c324a5e 100644 --- a/test/projectgraph/cases/javascript/dependency-same-path/summaries/npm_util-kit_1.0.0.json +++ b/test/projectgraph/cases/javascript/dependency-same-path/summaries/npm_util-kit_1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", - "index_version": "12", - "link_version": "12", + "index_version": "13", + "link_version": "13", "unit": "npm:util-kit@1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/javascript/dependency-transitive/summaries/npm_lodash._reinterpolate_3.0.0.json b/test/projectgraph/cases/javascript/dependency-transitive/summaries/npm_lodash._reinterpolate_3.0.0.json index 5990e0b2..2ec04cfe 100644 --- a/test/projectgraph/cases/javascript/dependency-transitive/summaries/npm_lodash._reinterpolate_3.0.0.json +++ b/test/projectgraph/cases/javascript/dependency-transitive/summaries/npm_lodash._reinterpolate_3.0.0.json @@ -1,7 +1,7 @@ { "version": "2", - "index_version": "12", - "link_version": "12", + "index_version": "13", + "link_version": "13", "unit": "npm:lodash._reinterpolate@3.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/javascript/dependency-transitive/summaries/npm_lodash_4.17.21.json b/test/projectgraph/cases/javascript/dependency-transitive/summaries/npm_lodash_4.17.21.json index f3662300..e835a375 100644 --- a/test/projectgraph/cases/javascript/dependency-transitive/summaries/npm_lodash_4.17.21.json +++ b/test/projectgraph/cases/javascript/dependency-transitive/summaries/npm_lodash_4.17.21.json @@ -1,7 +1,7 @@ { "version": "2", - "index_version": "12", - "link_version": "12", + "index_version": "13", + "link_version": "13", "unit": "npm:lodash@4.17.21", "symbols": [ { diff --git a/test/projectgraph/cases/javascript/references-argument/expect.yaml b/test/projectgraph/cases/javascript/references-argument/expect.yaml index 15ec72d2..edc1f654 100644 --- a/test/projectgraph/cases/javascript/references-argument/expect.yaml +++ b/test/projectgraph/cases/javascript/references-argument/expect.yaml @@ -1,5 +1,5 @@ source: safedep:hand -description: src/main.js passes the imported load to Array.map and the first-party parse to Promise.then. Each reference makes its function possibly called. The call of then is a call on the result of Promise.resolve, whose type the link step does not know, so the site keeps a record. +description: src/main.js passes the imported load to Array.map and the first-party parse to Promise.then. Each reference makes its function possibly called. The call of then is a call on the result of Promise.resolve, whose type the link step does not know with no code of Promise, so the site keeps a record with the reason no-dependency-source. roots: - key: src/main.js kind: manifest @@ -18,7 +18,7 @@ edges: unresolved: - caller: src/main.js site: src/main.js:5 - reason: unknown-receiver + reason: no-dependency-source method: then results: - target: js-yaml//load diff --git a/test/projectgraph/cases/python/dependency-dark-unit/summaries/pypi_PyYAML_6.0.1.json b/test/projectgraph/cases/python/dependency-dark-unit/summaries/pypi_PyYAML_6.0.1.json index 403e9132..111172ef 100644 --- a/test/projectgraph/cases/python/dependency-dark-unit/summaries/pypi_PyYAML_6.0.1.json +++ b/test/projectgraph/cases/python/dependency-dark-unit/summaries/pypi_PyYAML_6.0.1.json @@ -1,7 +1,7 @@ { "version": "2", - "index_version": "12", - "link_version": "12", + "index_version": "13", + "link_version": "13", "unit": "pypi:PyYAML@6.0.1", "symbols": [ { diff --git a/test/projectgraph/cases/python/dependency-failed-reached/summaries/pypi_badpkg_1.0.0.json b/test/projectgraph/cases/python/dependency-failed-reached/summaries/pypi_badpkg_1.0.0.json index 8a516a1e..35366ed5 100644 --- a/test/projectgraph/cases/python/dependency-failed-reached/summaries/pypi_badpkg_1.0.0.json +++ b/test/projectgraph/cases/python/dependency-failed-reached/summaries/pypi_badpkg_1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", - "index_version": "12", - "link_version": "12", + "index_version": "13", + "link_version": "13", "unit": "pypi:badpkg@1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/python/dependency-failed-reached/summaries/pypi_okpkg_1.0.0.json b/test/projectgraph/cases/python/dependency-failed-reached/summaries/pypi_okpkg_1.0.0.json index 0f95caeb..579a38ea 100644 --- a/test/projectgraph/cases/python/dependency-failed-reached/summaries/pypi_okpkg_1.0.0.json +++ b/test/projectgraph/cases/python/dependency-failed-reached/summaries/pypi_okpkg_1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", - "index_version": "12", - "link_version": "12", + "index_version": "13", + "link_version": "13", "unit": "pypi:okpkg@1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/python/dependency-failed-unreached/summaries/pypi_badpkg_1.0.0.json b/test/projectgraph/cases/python/dependency-failed-unreached/summaries/pypi_badpkg_1.0.0.json index 8a516a1e..35366ed5 100644 --- a/test/projectgraph/cases/python/dependency-failed-unreached/summaries/pypi_badpkg_1.0.0.json +++ b/test/projectgraph/cases/python/dependency-failed-unreached/summaries/pypi_badpkg_1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", - "index_version": "12", - "link_version": "12", + "index_version": "13", + "link_version": "13", "unit": "pypi:badpkg@1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/python/dependency-failed-unreached/summaries/pypi_okpkg_1.0.0.json b/test/projectgraph/cases/python/dependency-failed-unreached/summaries/pypi_okpkg_1.0.0.json index 0f95caeb..579a38ea 100644 --- a/test/projectgraph/cases/python/dependency-failed-unreached/summaries/pypi_okpkg_1.0.0.json +++ b/test/projectgraph/cases/python/dependency-failed-unreached/summaries/pypi_okpkg_1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", - "index_version": "12", - "link_version": "12", + "index_version": "13", + "link_version": "13", "unit": "pypi:okpkg@1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/python/dependency-package-dir/summaries/pypi_outer_1.0.0.json b/test/projectgraph/cases/python/dependency-package-dir/summaries/pypi_outer_1.0.0.json index 3bdc373c..e9345066 100644 --- a/test/projectgraph/cases/python/dependency-package-dir/summaries/pypi_outer_1.0.0.json +++ b/test/projectgraph/cases/python/dependency-package-dir/summaries/pypi_outer_1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", - "index_version": "12", - "link_version": "12", + "index_version": "13", + "link_version": "13", "unit": "pypi:outer@1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/python/dependency-site-packages/summaries/pypi_requests_2.31.0.json b/test/projectgraph/cases/python/dependency-site-packages/summaries/pypi_requests_2.31.0.json index df2da9d3..d5997158 100644 --- a/test/projectgraph/cases/python/dependency-site-packages/summaries/pypi_requests_2.31.0.json +++ b/test/projectgraph/cases/python/dependency-site-packages/summaries/pypi_requests_2.31.0.json @@ -1,7 +1,7 @@ { "version": "2", - "index_version": "12", - "link_version": "12", + "index_version": "13", + "link_version": "13", "unit": "pypi:requests@2.31.0", "symbols": [ { diff --git a/test/projectgraph/cases/python/dependency-site-packages/summaries/pypi_urllib3_2.0.7.json b/test/projectgraph/cases/python/dependency-site-packages/summaries/pypi_urllib3_2.0.7.json index 351b03cc..1a984add 100644 --- a/test/projectgraph/cases/python/dependency-site-packages/summaries/pypi_urllib3_2.0.7.json +++ b/test/projectgraph/cases/python/dependency-site-packages/summaries/pypi_urllib3_2.0.7.json @@ -1,7 +1,7 @@ { "version": "2", - "index_version": "12", - "link_version": "12", + "index_version": "13", + "link_version": "13", "unit": "pypi:urllib3@2.0.7", "symbols": [ { diff --git a/test/projectgraph/receivers_test.go b/test/projectgraph/receivers_test.go index cb89dd06..cfecdca5 100644 --- a/test/projectgraph/receivers_test.go +++ b/test/projectgraph/receivers_test.go @@ -76,56 +76,42 @@ var goReceiverRows = []receiverRow{ source: "func run() {\n\tt := dep.NewThing()\n\tt.Do()\n}", target: "example.com//dep//Thing//Do", want: "found", - gap: "FOU-543", - today: "uncertain/unknown-receiver", }, { name: "multi-return", source: "func run() {\n\tt, err := dep.NewPair()\n\tif err != nil {\n\t\treturn\n\t}\n\tt.Do()\n}", target: "example.com//dep//Thing//Do", want: "found", - gap: "FOU-543", - today: "uncertain/unknown-receiver", }, { name: "chained call", source: "func run() {\n\tdep.NewThing().Do()\n}", target: "example.com//dep//Thing//Do", want: "found", - gap: "FOU-543", - today: "uncertain/unknown-receiver", }, { name: "chained method call", source: "func run() {\n\tdep.NewThing().Next().Do()\n}", target: "example.com//dep//Thing//Do", want: "found", - gap: "FOU-543", - today: "uncertain/unknown-receiver", }, { name: "interface return", source: "func run() {\n\td := dep.NewDoer()\n\td.Do()\n}", target: "example.com//dep//Thing//Do", want: "found", - gap: "FOU-543", - today: "uncertain/unknown-receiver", }, { name: "interface return with no implementation", source: "func run() {\n\tr := dep.NewRunner()\n\tr.Run()\n}", target: "example.com//other//Job//Run", want: "uncertain/interface-receiver", - gap: "FOU-543", - today: "uncertain/unknown-receiver", }, { name: "external function return with no dependency code", source: "func run() {\n\tt := dep.NewThing()\n\tt.Do()\n}", target: "example.com//dep//Thing//Do", want: "uncertain/no-dependency-source", - gap: "FOU-543", - today: "uncertain/unknown-receiver", noDeps: true, }, } diff --git a/test/projectgraph/report.md b/test/projectgraph/report.md index ebc2e78a..8a5740fa 100644 --- a/test/projectgraph/report.md +++ b/test/projectgraph/report.md @@ -901,29 +901,67 @@ wins over another reason. Else the cause is the first uncertainty of the result, as `budget`, `ambiguous` or `computed-name`. A library project has no main, so every exported symbol is a root. -### Before - -| Language | Kind | Run | Targets | found | not-found | no-such-target | unknown-receiver | no-definition | budget | ambiguous | computed-name | -| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | -| Go | function | before | 256 | 13 | 0 | 7 | 3 | 1 | 145 | 53 | 34 | -| Go | method | before | 1298 | 29 | 2 | 15 | 294 | 119 | 576 | 158 | 105 | -| Rust | function | before | 13 | 0 | 0 | 10 | 3 | 0 | 0 | 0 | 0 | -| Rust | method | before | 38 | 0 | 1 | 19 | 13 | 5 | 0 | 0 | 0 | -| Rust | other | before | 5 | 0 | 0 | 5 | 0 | 0 | 0 | 0 | 0 | +### Results + +The "before" rows come from the commit of FOU-542, before any fix of this +measure. The "FOU-543" rows come from the commit of FOU-543, which also +holds the fixes of FOU-545, FOU-546 and FOU-547 before it. The Rust rows of +FOU-543 do not change, so the table leaves them out. + +| Language | Kind | Run | Targets | found | not-found | no-such-target | unknown-receiver | no-definition | interface-receiver | no-dependency-source | budget | ambiguous | computed-name | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| Go | function | before | 256 | 13 | 0 | 7 | 3 | 1 | 0 | 0 | 145 | 53 | 34 | +| Go | function | FOU-543 | 256 | 13 | 0 | 7 | 3 | 0 | 0 | 3 | 39 | 132 | 59 | +| Go | method | before | 1298 | 29 | 2 | 15 | 294 | 119 | 0 | 0 | 576 | 158 | 105 | +| Go | method | FOU-543 | 1298 | 30 | 2 | 15 | 319 | 112 | 3 | 2 | 123 | 459 | 233 | +| Rust | function | before | 13 | 0 | 0 | 10 | 3 | 0 | 0 | 0 | 0 | 0 | 0 | +| Rust | method | before | 38 | 0 | 1 | 19 | 13 | 5 | 0 | 0 | 0 | 0 | 0 | +| Rust | other | before | 5 | 0 | 0 | 5 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | + +| Language | Run | Method targets that end as unknown-receiver | +| --- | --- | --- | +| Go | before | 0.227 (294/1298) | +| Go | FOU-543 | 0.246 (319/1298) | +| Rust | before | 0.342 (13/38) | -The share of the method targets that end as `unknown-receiver` is 0.227 -(294/1298) for Go and 0.342 (13/38) for Rust. +Before: - Go: `budget` is the largest cause. The index of each `idna/tables*.go` of x/net hits a limit, so the file is truncated. Each `gen.go` of x/net has `//go:build ignore` and `package main`, so the link step records a truncation for it. A `_test.go` file of x/crypto is truncated too. The budget of a file of a unit concerns every target - once the search enters the unit. - hey has 47 truncations and scany 55. + once the search enters the unit. hey has 47 truncations and scany 55. - Go: an `ambiguous` or a `computed-name` call concerns every target. These calls come from the code of a dependency unit, as pgx and amqp091-go, and from the project, as fiber and go-grpc-middleware. - Rust: the link step does not link the code of a `cargo:` unit, so the graph holds no edge into the crate. A target that no project file names is `no-such-target`. + +After FOU-543: + +- Go: no project has a truncation of the link step. The graphs of the 10 + Go projects hold 3,435 files, 44% of the 7,810 of before, because the + index step leaves out the test files and the files that the build of + linux/amd64 does not read. `budget` + falls from 576 to 123 method targets. The rest is scany: two + `tables.go` files of x/text are over the size limit of the index step, + so they fail. +- Go: with less budget, the first uncertainty of a result is more often + an `ambiguous` or a `computed-name` call of the code of a unit, which + concerns every target. So these columns grow, and the share of + `unknown-receiver` grows a little. The search reaches more code of the + units through the new edges, and more call sites with an unknown + receiver. +- Go: `found` moves from 29 to 30 method targets. Most symbols of an + advisory are methods that the code of the dependency calls inside its + own module, as the methods of the HTTP/2 server of x/net. A call inside + a unit goes to the definition of the method and not to its external key, + so the search reaches the definition and the target, which is the + external key, reads as uncertain. The receiver forms of FOU-543 make the + calls of the project find the method, as the receiver table and the + cases show. They do not change how a target matches code inside a unit. +- The measure has no target of the standard library. The Go vulndb has + the most method symbols for it, and FOU-543 adds `GOROOT/src` as a unit, + but the corpus pins no Go release. diff --git a/test/projectgraph/version_test.go b/test/projectgraph/version_test.go index 48d4c936..946ad323 100644 --- a/test/projectgraph/version_test.go +++ b/test/projectgraph/version_test.go @@ -27,6 +27,7 @@ var graphHashes = map[string]string{ "11/11": "19e0527969cf2702f5abfab8234310b254ac25a6a539ab6e86cf6febabfb8757", "11/12": "14d5bbbaca07ab950f75c665f48e71ce9195da2ebd853cd1b0b745d42c4852cc", "12/12": "1cb170ec83fe3517e6f71755983c040210f83bb7a0e69528b3d7007dcc69d5dc", + "13/13": "9383a0bc5c6e8b760beb443112cae1400c39212c1da5c2d155c22488eb00eaa6", } // resultHashes records the hash of the reach results of every case for @@ -38,6 +39,7 @@ var resultHashes = map[string]string{ "11/11/2": "58d8fd9c5b67bf02a44778a5173828515c9424a1fddd9ff3f644d8fc13ebcfbb", "11/12/2": "8341f2a1965f9d3b19d976ff1ba63ed26c1e15026e02079ec6195dfc0d762dba", "12/12/2": "b89a142078d885eeb73fe15c731315c60d42b38270efd12a7857be44b9ccc83c", + "13/13/2": "2f81b00e1708cbb7e0589f34eaa7866a5658a6cfc0a7608d532381ca4267ba06", } // summaryHashes records the hash of the golden summaries of the cases for @@ -50,6 +52,7 @@ var summaryHashes = map[string]string{ "11/11/2": "c0aeb7f515b2c546fd6b355663d5a7bb446c7f6bdfb9ab3ef343dd252e4302bf", "11/12/2": "f4102bd40a876fa387f7ff1ce81505e1f04b64cd8165602d0726f897e2b88a7f", "12/12/2": "cb2147fda018110f38254519bbc75a814b99e0318f5c7485fbce78a667bb4287", + "13/13/2": "dc844b1cd13e7873f54d646f27384c78b64707e13ae9c9ef27a4557ba7ba4dfc", } func TestGraphHashFollowsVersion(t *testing.T) { From b83cc3f04b725b4cbe694d93b17dc078a471b7fd Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 19 Oct 2026 08:34:05 +0000 Subject: [PATCH 6/9] feat(projectgraph): link Rust crates and bind the receiver from result, reference and generic types (FOU-544) A -deps run with a crate printed "the link step does not link the rust code of cargo:smallvec@1.13.2", and the link step knew a Rust receiver type only from Type::new. So a call on a parameter, a field, a result or a clone of a type of a crate read as unknown-receiver. The link step now links the Rust code of a cargo: unit. Each space has its own crates, and the root module of a crate of a unit is the unit, so two versions of one crate stay apart. A path of another crate, with or without a use, records its crate as the origin of the key, and the unit that gives the crate links it. The external key of an item of a unit is the path that another crate writes, with the shortest pub use of the root. A Rust unit has a surface, so a summary lists its symbols. The Rust index records the return type of each function and method, with Self as the type of the impl. Option and Result keep T in Returns and the wrapper in the new field Wraps. unwrap and expect give T, and so does a ?, which the index does not record. Another method of the wrapper gives no type. A declared type of a parameter, a field or a local that a unit gives binds the receiver, and an associated function binds its result. clone, to_owned, default and from on a type of a unit keep the type also when the crate derives them. A trait of a unit, as a trait object or a generic bound, gets the cha edges to its impls through their external keys, or a record with the new reason trait-receiver. The Rust rows of the receiver table now pass, and each probe of the real smallvec 1.13.2 finds SmallVec::insert_many. The case rust/dependency-crate checks a crate with a private module, a Result that unwrap opens and a generic bound. report.md has the numbers after this change. The govulncheck set still finds 9 of 9 stacks. fileindex.Version is 14 and projectgraph.Version is 14. The cache of the type that a unit gives for a key now skips an answer that the cycle guard of unitDefinitions shaped. An outer walk of the same name made it miss a definition, and the cache kept the miss for later queries, so the answer depended on the order of the walk. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01PFQbqPqd9xn1fT6hBsyoUm Signed-off-by: Claude --- docs/projectgraph.md | 29 ++- examples/projectgraph/main.go | 3 +- fileindex/index.go | 13 +- fileindex/testdata/index.json | 2 +- plugin/indexer/plugin_test.go | 1 + plugin/indexer/rust.go | 18 +- plugin/indexer/testdata/golden/App.cs.json | 2 +- plugin/indexer/testdata/golden/App.java.json | 2 +- plugin/indexer/testdata/golden/App.kt.json | 2 +- plugin/indexer/testdata/golden/app.go.json | 2 +- plugin/indexer/testdata/golden/app.js.json | 2 +- plugin/indexer/testdata/golden/app.php.json | 2 +- plugin/indexer/testdata/golden/app.py.json | 2 +- plugin/indexer/testdata/golden/app.rb.json | 2 +- plugin/indexer/testdata/golden/app.rs.json | 4 +- plugin/indexer/testdata/golden/app.ts.json | 2 +- .../indexer/testdata/golden/exports.js.json | 2 +- .../indexer/testdata/golden/exports.py.json | 2 +- .../indexer/testdata/golden/exports.ts.json | 2 +- plugin/indexer/testdata/golden/flags.js.json | 2 +- plugin/indexer/testdata/golden/flags.py.json | 2 +- plugin/indexer/testdata/golden/flags.ts.json | 2 +- projectgraph/declared.go | 50 ++++- projectgraph/golang.go | 1 + projectgraph/graph.go | 2 +- projectgraph/link.go | 4 + projectgraph/packages.go | 8 +- projectgraph/profile.go | 12 ++ projectgraph/resolve.go | 6 +- projectgraph/results.go | 82 ++++++-- projectgraph/rust.go | 197 ++++++++++++++++-- .../javascript/barrel-chain/graph.golden.json | 2 +- .../javascript/barrel-chain/indexes.json | 8 +- .../commonjs-require/graph.golden.json | 2 +- .../javascript/commonjs-require/indexes.json | 6 +- .../default-import/graph.golden.json | 2 +- .../javascript/default-import/indexes.json | 6 +- .../esm-named-import/graph.golden.json | 2 +- .../javascript/esm-named-import/indexes.json | 6 +- .../javascript/export-star/graph.golden.json | 2 +- .../javascript/export-star/indexes.json | 10 +- .../exports-conditions/graph.golden.json | 2 +- .../exports-conditions/indexes.json | 8 +- .../unknown-receiver/graph.golden.json | 2 +- .../javascript/unknown-receiver/indexes.json | 2 +- .../javascript/workspaces/graph.golden.json | 2 +- .../javascript/workspaces/indexes.json | 6 +- .../python/all-hides/graph.golden.json | 2 +- .../testdata/python/all-hides/indexes.json | 6 +- .../python/assignment/graph.golden.json | 2 +- .../testdata/python/assignment/indexes.json | 2 +- .../python/class-body/graph.golden.json | 2 +- .../testdata/python/class-body/indexes.json | 2 +- .../python/decorators/graph.golden.json | 2 +- .../testdata/python/decorators/indexes.json | 2 +- .../python/external/graph.golden.json | 2 +- .../testdata/python/external/indexes.json | 2 +- .../python/local-receiver/graph.golden.json | 2 +- .../python/local-receiver/indexes.json | 2 +- .../python/member-call/graph.golden.json | 2 +- .../testdata/python/member-call/indexes.json | 6 +- .../python/missing-file/graph.golden.json | 2 +- .../testdata/python/missing-file/indexes.json | 2 +- .../python/package-init/graph.golden.json | 2 +- .../testdata/python/package-init/indexes.json | 6 +- .../python/reexport-cycle/graph.golden.json | 2 +- .../python/reexport-cycle/indexes.json | 6 +- .../python/relative-imports/graph.golden.json | 2 +- .../python/relative-imports/indexes.json | 6 +- .../python/sibling-import/graph.golden.json | 2 +- .../python/sibling-import/indexes.json | 4 +- .../python/src-layout/graph.golden.json | 2 +- .../testdata/python/src-layout/indexes.json | 6 +- .../python/sys-path/graph.golden.json | 2 +- .../testdata/python/sys-path/indexes.json | 4 +- .../python/wildcard/graph.golden.json | 2 +- .../testdata/python/wildcard/indexes.json | 4 +- .../roots/extra-rules/graph.golden.json | 2 +- .../testdata/roots/extra-rules/indexes.json | 6 +- .../roots/library-surface/graph.golden.json | 2 +- .../roots/library-surface/indexes.json | 4 +- .../roots/main-guard/graph.golden.json | 2 +- .../testdata/roots/main-guard/indexes.json | 4 +- .../roots/package-json-bin/graph.golden.json | 2 +- .../roots/package-json-bin/indexes.json | 4 +- .../roots/package-json-main/graph.golden.json | 2 +- .../roots/package-json-main/indexes.json | 8 +- .../procfile-dockerfile/graph.golden.json | 2 +- .../roots/procfile-dockerfile/indexes.json | 12 +- .../roots/pyproject-scripts/graph.golden.json | 2 +- .../roots/pyproject-scripts/indexes.json | 10 +- .../testdata/roots/setup-py/graph.golden.json | 2 +- .../testdata/roots/setup-py/indexes.json | 4 +- .../roots/test-file/graph.golden.json | 2 +- .../testdata/roots/test-file/indexes.json | 2 +- .../typescript/js-specifier/graph.golden.json | 2 +- .../typescript/js-specifier/indexes.json | 4 +- .../tsconfig-paths/graph.golden.json | 2 +- .../typescript/tsconfig-paths/indexes.json | 6 +- .../typescript/type-only/graph.golden.json | 2 +- .../typescript/type-only/indexes.json | 6 +- projectgraph/walk.go | 49 +++-- .../go_github.com_acme_kit_v1.0.0.json | 4 +- .../go_github.com_acme_kit_v1.0.0.json | 4 +- .../go_github.com_acme_dep_v1.0.0.json | 4 +- .../go_github.com_acme_yamlish_v1.2.0.json | 4 +- .../go_github.com_acme_kit_v1.0.0.json | 4 +- .../go_example.com_markup_v1.0.0.json | 4 +- .../summaries/go_std_go1.25.1.json | 4 +- .../summaries/npm_store-kit_1.0.0.json | 4 +- .../summaries/npm_kit_1.0.0.json | 4 +- .../summaries/npm_kit_1.0.0.json | 4 +- .../summaries/npm_plug_1.0.0.json | 4 +- .../summaries/npm_util-kit_1.0.0.json | 4 +- .../npm_lodash._reinterpolate_3.0.0.json | 4 +- .../summaries/npm_lodash_4.17.21.json | 4 +- .../summaries/pypi_PyYAML_6.0.1.json | 4 +- .../summaries/pypi_badpkg_1.0.0.json | 4 +- .../summaries/pypi_okpkg_1.0.0.json | 4 +- .../summaries/pypi_badpkg_1.0.0.json | 4 +- .../summaries/pypi_okpkg_1.0.0.json | 4 +- .../summaries/pypi_outer_1.0.0.json | 4 +- .../summaries/pypi_requests_2.31.0.json | 4 +- .../summaries/pypi_urllib3_2.0.7.json | 4 +- .../cases/rust/dependency-crate/expect.yaml | 170 +++++++++++++++ .../rust/dependency-crate/project/Cargo.toml | 7 + .../dependency-crate/project/src/flush.rs | 5 + .../rust/dependency-crate/project/src/main.rs | 9 + .../project/vendor/kit/Cargo.toml | 4 + .../project/vendor/kit/src/lib.rs | 7 + .../project/vendor/kit/src/store.rs | 21 ++ .../summaries/cargo_kit_1.0.0.json | 74 +++++++ test/projectgraph/receivers_test.go | 26 --- test/projectgraph/report.md | 55 ++++- test/projectgraph/version_test.go | 3 + 135 files changed, 943 insertions(+), 289 deletions(-) create mode 100644 test/projectgraph/cases/rust/dependency-crate/expect.yaml create mode 100644 test/projectgraph/cases/rust/dependency-crate/project/Cargo.toml create mode 100644 test/projectgraph/cases/rust/dependency-crate/project/src/flush.rs create mode 100644 test/projectgraph/cases/rust/dependency-crate/project/src/main.rs create mode 100644 test/projectgraph/cases/rust/dependency-crate/project/vendor/kit/Cargo.toml create mode 100644 test/projectgraph/cases/rust/dependency-crate/project/vendor/kit/src/lib.rs create mode 100644 test/projectgraph/cases/rust/dependency-crate/project/vendor/kit/src/store.rs create mode 100644 test/projectgraph/cases/rust/dependency-crate/summaries/cargo_kit_1.0.0.json diff --git a/docs/projectgraph.md b/docs/projectgraph.md index b5d9e068..a60dd92d 100644 --- a/docs/projectgraph.md +++ b/docs/projectgraph.md @@ -163,8 +163,8 @@ of the highest confidence. A call that the link step cannot resolve is an unresolved site with a reason: `computed-name`, `dynamic-import`, `unknown-receiver`, -`no-definition`, `ambiguous`, `limit`, `interface-receiver` or -`no-dependency-source`. The site keeps the method name of the call, so the +`no-definition`, `ambiguous`, `limit`, `interface-receiver`, +`trait-receiver` or `no-dependency-source`. The site keeps the method name of the call, so the reach query can tell which targets it concerns. A call on the result of another call, as `html.NewTokenizer(r).Next()`, @@ -192,6 +192,31 @@ edge joins the key to the definition in the unit. A chained call, as | A function of a package that no unit gives | A record with the reason `no-dependency-source` | | A result with no named type, or a function that the unit does not define | A record with the reason `unknown-receiver` | +Rust follows the same rules, with these additions. + +- The link step links the code of a `cargo:` unit, as the crate directory + in the cargo registry or in `vendor/`. The root module of the crate is + the unit, so two versions of one crate stay apart. The external key of + an item is the path that another crate writes, and an item of a private + module that the root gives on with `pub use` gets the shortest path, as + `smallvec//SmallVec`. +- The index records the return type of each function and method, with + `Self` as the type of its `impl`. A type drops `&`, `&mut`, `Box<..>`, + `Rc<..>`, `Arc<..>` and its type arguments, so `&mut SmallVec<[u8; 4]>` + is `SmallVec`. An `Option` or a `Result` result keeps `T` in + `Returns` and its wrapper in `Wraps`. `unwrap` and `expect` give `T`, + and so does a `?`, which the index does not record. A call of another + method of the wrapper, as `map`, has no type. +- A declared type of a parameter, a field or a local that a unit gives + binds the receiver. An associated function, as `Type::from_vec(v)`, + binds the type that it returns, and `clone`, `to_owned`, `default` and + `from` on a type of a unit keep the type also when the crate derives + them. +- A trait object or a generic bound `T: Trait` of a unit gets the `cha` + edges to the implementations in the graph, through their external keys. + With no implementation, the site is a record with the reason + `trait-receiver`. + The standard library is a unit too. Pass `GOROOT/src` as the unit `go:std@`, as `-deps $(go env GOROOT)/src=go:std@go1.25.1`. It gives each import path whose first element has no dot, as `net/http`. The index diff --git a/examples/projectgraph/main.go b/examples/projectgraph/main.go index e27d3950..97cfb662 100644 --- a/examples/projectgraph/main.go +++ b/examples/projectgraph/main.go @@ -14,7 +14,8 @@ // keeps its path under the import directory, as yaml/__init__.py. Any // other -deps directory is an import directory of its own, and a pypi // package given that way is its package. The Go standard library is a unit -// too, as -deps $(go env GOROOT)/src=go:std@go1.25.1. With -summary it +// too, as -deps $(go env GOROOT)/src=go:std@go1.25.1, and so is a Rust +// crate, as -deps vendor/smallvec=cargo:smallvec@1.13.2. With -summary it // prints the summary of each such unit as JSON. package main diff --git a/fileindex/index.go b/fileindex/index.go index 5ed99c55..53c8de84 100644 --- a/fileindex/index.go +++ b/fileindex/index.go @@ -19,7 +19,7 @@ import ( // Version is the version of the index records and of the rules that fill // them. Bump it when a field, a kind, an order or a fill rule changes. A // store keys an index by Version, so the bump makes every older index miss. -const Version = "13" +const Version = "14" // DefaultTestPaths are the globs that mark the test files of the common // layouts, as projectgraph.Options.TestPaths takes them. A glob with no "/" @@ -91,6 +91,9 @@ const ( // UnresolvedInterfaceReceiver is a call on a value of an interface // type that no type of the graph implements, so its code is elsewhere. UnresolvedInterfaceReceiver UnresolvedReason = "interface-receiver" + // UnresolvedTraitReceiver is a call on a Rust trait object or a value + // of a generic bound that no type of the graph implements. + UnresolvedTraitReceiver UnresolvedReason = "trait-receiver" // UnresolvedNoDependencySource is a call on a value whose type comes // from a package with no code in the graph, as the result of a // function of a module that no unit gives. @@ -213,8 +216,10 @@ type Export struct { // method: the count of its parameters, a last parameter of the form ...T, // and the type name of its first result. Results holds the type name of // each result of a Go function with two or more results, by position, and -// "" for a result with no named type. Static marks a static method of -// Java. +// "" for a result with no named type. Wraps names the Option or the Result +// that holds the result of a Rust function, and Returns then names the +// type inside, as Thing for -> Option. Static marks a static method +// of Java. type Definition struct { Key string `json:"key"` Kind DefKind `json:"kind"` @@ -231,6 +236,7 @@ type Definition struct { Variadic bool `json:"variadic,omitempty"` Returns string `json:"returns,omitempty"` Results []string `json:"results,omitempty"` + Wraps string `json:"wraps,omitempty"` Static bool `json:"static,omitempty"` Flags []Flag `json:"flags,omitempty"` } @@ -613,6 +619,7 @@ func (ix *Index) EachString(fn func(string) string) { typed(def.Locals) kind(&def.Returns) strs(def.Results) + kind(&def.Wraps) flags(def.Flags) } for i := range ix.Classes { diff --git a/fileindex/testdata/index.json b/fileindex/testdata/index.json index 35ca47db..bfd539ee 100644 --- a/fileindex/testdata/index.json +++ b/fileindex/testdata/index.json @@ -1,5 +1,5 @@ { - "version": "13", + "version": "14", "path": "src/app.py", "language": "python", "content_hash": "a172cedcae47474b615c54d510a5d84a8dea3032e958587430b413538be3f333", diff --git a/plugin/indexer/plugin_test.go b/plugin/indexer/plugin_test.go index b2959272..175c5883 100644 --- a/plugin/indexer/plugin_test.go +++ b/plugin/indexer/plugin_test.go @@ -102,6 +102,7 @@ var goldenHashes = map[string]string{ "11": "141e6d6a6f6d369635bf4f6781033acc269e59fe5f722558192b8e952c7e7bd9", "12": "974943df4185f9c3070dcfdfedff8e91912e257d255a936f7c27608bb63a69f0", "13": "97614d8b96782e65813ee409e4a3f33c4655157b5dfa58baf17cd3677958c619", + "14": "26f22c80fd4120cd04f228a53188accb1695434d0ed1ff5654b110981f8f44dd", } func TestFileIndexGoldenHashFollowsVersion(t *testing.T) { diff --git a/plugin/indexer/rust.go b/plugin/indexer/rust.go index cfa2e598..610908d9 100644 --- a/plugin/indexer/rust.go +++ b/plugin/indexer/rust.go @@ -194,6 +194,22 @@ func (p *projection) rustTypeName(node *sitter.Node, bounds map[string]string) s return "" } +// rustWrappers are the types of std whose value ?, unwrap and expect give, +// as T of Option and of Result. +var rustWrappers = []string{"Option", "Result"} + +// rustResult returns the type that a result names, and the Option or the +// Result that holds it, as Thing and Option for Option>. +func (p *projection) rustResult(node *sitter.Node, bounds map[string]string) (string, string) { + if node != nil && node.Type() == "generic_type" { + name := p.rustTypeName(node.ChildByFieldName("type"), bounds) + if args := ts.NamedChildren(node.ChildByFieldName("type_arguments")); slices.Contains(rustWrappers, name) && len(args) > 0 { + return p.rustTypeName(args[0], bounds), name + } + } + return p.rustTypeName(node, bounds), "" +} + // rustBounds returns the first trait bound of each type parameter of a // function and of its where clause. func (p *projection) rustBounds(fn *sitter.Node) map[string]string { @@ -259,7 +275,7 @@ func rustSignature(p *projection, def *fileindex.Definition, node *sitter.Node) def.Params = append(def.Params, fileindex.Typed{Name: p.text(name), Type: typ}) } } - def.Returns = p.rustTypeName(node.ChildByFieldName("return_type"), bounds) + def.Returns, def.Wraps = p.rustResult(node.ChildByFieldName("return_type"), bounds) def.Locals = p.rustLocals(node.ChildByFieldName("body"), bounds) if impl := rustImpl(node); impl != nil && !def.Static && !p.rustDefinesType(impl) { def.Params = append([]fileindex.Typed{{Name: extensionParam, Type: p.rustItemName(impl)}}, def.Params...) diff --git a/plugin/indexer/testdata/golden/App.cs.json b/plugin/indexer/testdata/golden/App.cs.json index 9bcd0d82..7a21f6ce 100644 --- a/plugin/indexer/testdata/golden/App.cs.json +++ b/plugin/indexer/testdata/golden/App.cs.json @@ -1,5 +1,5 @@ { - "version": "13", + "version": "14", "path": "App.cs", "language": "csharp", "content_hash": "8991919e54610418b58a53fb6b26dc563c5ef5af684165b60e8af5b6e7e8773c", diff --git a/plugin/indexer/testdata/golden/App.java.json b/plugin/indexer/testdata/golden/App.java.json index 057a6be8..0ea48aa0 100644 --- a/plugin/indexer/testdata/golden/App.java.json +++ b/plugin/indexer/testdata/golden/App.java.json @@ -1,5 +1,5 @@ { - "version": "13", + "version": "14", "path": "App.java", "language": "java", "content_hash": "4b93f5c2c4ed8e1881cb857a9b7cd65c3a0d4cb4cd0929140539d0a418a27a95", diff --git a/plugin/indexer/testdata/golden/App.kt.json b/plugin/indexer/testdata/golden/App.kt.json index a9e81596..220cf808 100644 --- a/plugin/indexer/testdata/golden/App.kt.json +++ b/plugin/indexer/testdata/golden/App.kt.json @@ -1,5 +1,5 @@ { - "version": "13", + "version": "14", "path": "App.kt", "language": "kotlin", "content_hash": "458147a6c6ebc2561187207bb3bca7e091914bfd2e52f0540f58887976983462", diff --git a/plugin/indexer/testdata/golden/app.go.json b/plugin/indexer/testdata/golden/app.go.json index 0c27098c..1ed55cf9 100644 --- a/plugin/indexer/testdata/golden/app.go.json +++ b/plugin/indexer/testdata/golden/app.go.json @@ -1,5 +1,5 @@ { - "version": "13", + "version": "14", "path": "app.go", "language": "go", "content_hash": "3d65f36ed0edfd5e009c06d07d3d038337e9faebaca6c9762b5c473a14826166", diff --git a/plugin/indexer/testdata/golden/app.js.json b/plugin/indexer/testdata/golden/app.js.json index 27e177dc..d35c1491 100644 --- a/plugin/indexer/testdata/golden/app.js.json +++ b/plugin/indexer/testdata/golden/app.js.json @@ -1,5 +1,5 @@ { - "version": "13", + "version": "14", "path": "app.js", "language": "javascript", "content_hash": "247fdd512217fb5e7ce19cbf3f60b327e145abc15a26ee510e167cbc651b0a54", diff --git a/plugin/indexer/testdata/golden/app.php.json b/plugin/indexer/testdata/golden/app.php.json index 093123ee..22c23723 100644 --- a/plugin/indexer/testdata/golden/app.php.json +++ b/plugin/indexer/testdata/golden/app.php.json @@ -1,5 +1,5 @@ { - "version": "13", + "version": "14", "path": "app.php", "language": "php", "content_hash": "b57d8d52aafac8d7cbe2d19e5889e4e7b851ee71a48ea966cdd9cbfd14214466", diff --git a/plugin/indexer/testdata/golden/app.py.json b/plugin/indexer/testdata/golden/app.py.json index 9a29ae43..67e1a583 100644 --- a/plugin/indexer/testdata/golden/app.py.json +++ b/plugin/indexer/testdata/golden/app.py.json @@ -1,5 +1,5 @@ { - "version": "13", + "version": "14", "path": "app.py", "language": "python", "content_hash": "a9cdb5fb012d2a29970c07deda9cc8360983015e2360cb9091bca7bfce5c801d", diff --git a/plugin/indexer/testdata/golden/app.rb.json b/plugin/indexer/testdata/golden/app.rb.json index 5b97f003..ed1ac686 100644 --- a/plugin/indexer/testdata/golden/app.rb.json +++ b/plugin/indexer/testdata/golden/app.rb.json @@ -1,5 +1,5 @@ { - "version": "13", + "version": "14", "path": "app.rb", "language": "ruby", "content_hash": "f3a3e601660e0130750119df4267cac37972c412ccc8f5d8f16e3ba516f659d9", diff --git a/plugin/indexer/testdata/golden/app.rs.json b/plugin/indexer/testdata/golden/app.rs.json index fbd807eb..a57077f2 100644 --- a/plugin/indexer/testdata/golden/app.rs.json +++ b/plugin/indexer/testdata/golden/app.rs.json @@ -1,5 +1,5 @@ { - "version": "13", + "version": "14", "path": "app.rs", "language": "rust", "content_hash": "f07a751f9610f47e7b49679223d4ca80f85ecb21abde61488935d2f21a5c3603", @@ -76,7 +76,7 @@ "type": "String" } ], - "returns": "Result" + "wraps": "Result" }, { "key": "app.rs//App//audit", diff --git a/plugin/indexer/testdata/golden/app.ts.json b/plugin/indexer/testdata/golden/app.ts.json index 383c7bc5..9e409080 100644 --- a/plugin/indexer/testdata/golden/app.ts.json +++ b/plugin/indexer/testdata/golden/app.ts.json @@ -1,5 +1,5 @@ { - "version": "13", + "version": "14", "path": "app.ts", "language": "typescript", "content_hash": "d3908948ab8ec7b3adc898e0707ce74bb3a2d6393a724dfbeb67147840397234", diff --git a/plugin/indexer/testdata/golden/exports.js.json b/plugin/indexer/testdata/golden/exports.js.json index 6475eb62..5e68f690 100644 --- a/plugin/indexer/testdata/golden/exports.js.json +++ b/plugin/indexer/testdata/golden/exports.js.json @@ -1,5 +1,5 @@ { - "version": "13", + "version": "14", "path": "exports.js", "language": "javascript", "content_hash": "765716b5977b05d7bbc4ef5e0192e196e0209400366036053df632d54fdb0f95", diff --git a/plugin/indexer/testdata/golden/exports.py.json b/plugin/indexer/testdata/golden/exports.py.json index 15577c3f..0acadd05 100644 --- a/plugin/indexer/testdata/golden/exports.py.json +++ b/plugin/indexer/testdata/golden/exports.py.json @@ -1,5 +1,5 @@ { - "version": "13", + "version": "14", "path": "exports.py", "language": "python", "content_hash": "653115ddc22536763049e9209d4942bca5c6817b98bc01722879b69c04d8605b", diff --git a/plugin/indexer/testdata/golden/exports.ts.json b/plugin/indexer/testdata/golden/exports.ts.json index 81963b85..78e7f72a 100644 --- a/plugin/indexer/testdata/golden/exports.ts.json +++ b/plugin/indexer/testdata/golden/exports.ts.json @@ -1,5 +1,5 @@ { - "version": "13", + "version": "14", "path": "exports.ts", "language": "typescript", "content_hash": "0edcb9324080b924f3ac45d57f5b5cbe90d625038a0dd7f99fa9e5fcbeee1edc", diff --git a/plugin/indexer/testdata/golden/flags.js.json b/plugin/indexer/testdata/golden/flags.js.json index ebaeaa2f..78abeb49 100644 --- a/plugin/indexer/testdata/golden/flags.js.json +++ b/plugin/indexer/testdata/golden/flags.js.json @@ -1,5 +1,5 @@ { - "version": "13", + "version": "14", "path": "flags.js", "language": "javascript", "content_hash": "70731d0448bc5af9ecf5c81156eefbd39535a3fc674fffc250937d76c2ba4610", diff --git a/plugin/indexer/testdata/golden/flags.py.json b/plugin/indexer/testdata/golden/flags.py.json index 34f3d01c..3175a29a 100644 --- a/plugin/indexer/testdata/golden/flags.py.json +++ b/plugin/indexer/testdata/golden/flags.py.json @@ -1,5 +1,5 @@ { - "version": "13", + "version": "14", "path": "flags.py", "language": "python", "content_hash": "d3169e0c3c00508c3f9492866cf4db99ddd780d42dc9e4438558d1d4a22f2621", diff --git a/plugin/indexer/testdata/golden/flags.ts.json b/plugin/indexer/testdata/golden/flags.ts.json index a2017009..74721a1c 100644 --- a/plugin/indexer/testdata/golden/flags.ts.json +++ b/plugin/indexer/testdata/golden/flags.ts.json @@ -1,5 +1,5 @@ { - "version": "13", + "version": "14", "path": "flags.ts", "language": "typescript", "content_hash": "a84c4ad21735c1c250e33317463237749489ea1f220a93f97e2bea3f07b040c4", diff --git a/projectgraph/declared.go b/projectgraph/declared.go index abe06224..6dbb6c11 100644 --- a/projectgraph/declared.go +++ b/projectgraph/declared.go @@ -32,7 +32,13 @@ func (b *builder) typedName(sc *scope, scopeKey, name string) (string, fileindex if !ok { return "", fileindex.UnresolvedUnknownReceiver, false } - return b.resultType(fileindex.PackageOf(sc.ix.Path), fn, t.Index) + res, reason, ok := b.resultType(fileindex.PackageOf(sc.ix.Path), fn, t.Index) + // A name that holds a whole Option or Result has no + // type of a method of the value inside. + if ok && res.wraps != "" { + return "", fileindex.UnresolvedUnknownReceiver, false + } + return res.class, reason, ok } } } @@ -75,7 +81,11 @@ func (b *builder) fieldType(class, name string) (string, bool) { // first-party class or interface. A type of a dependency narrows nothing, as an // instance of a project class can extend it. A Go value holds its declared // type or, through an interface, a type of the project, so a Go type of a -// dependency gives the external key of its methods. +// dependency gives the external key of its methods. A type that a unit of +// dependency code gives narrows in any language with no subclass, as a +// Rust struct: a class gives its external key, and an interface or a trait +// gives its definition, whose implementations the class hierarchy analysis +// reads. func (b *builder) resolveType(sc *scope, from, text string) (string, bool) { if sc == nil { return "", false @@ -86,12 +96,44 @@ func (b *builder) resolveType(sc *scope, from, text string) (string, bool) { return "", false case b.isType(key): return key, true - case sc.profile.implicitInterfaces && b.kindOf(key) == "" && b.g.IsExternal(key) && strings.Contains(key, fileindex.Separator): - return key, true + case b.g.IsExternal(key) && strings.Contains(key, fileindex.Separator): + if t, ok := b.unitType(key); ok && (sc.profile.implicitInterfaces || sc.profile.unitTypes) { + return t, true + } + return key, sc.profile.implicitInterfaces && b.kindOf(key) == "" } return "", false } +// unitType returns the type that a unit of dependency code gives for an +// external key: the key itself for a class, or the definition of an +// interface or a trait. +func (b *builder) unitType(key string) (string, bool) { + if t, ok := b.unitTypes[key]; ok { + return t, t != "" + } + t := "" + defs, _ := b.unitDefinitions(b.origins[key]) + for _, d := range defs { + switch b.kindOf(d) { + case NodeInterface: + t = d + case NodeClass: + t = key + default: + continue + } + break + } + // unitDefinitions skips a name that an outer walk resolves now, so an + // answer found under the cycle guard can miss a definition. It stays + // out of the cache, or a later query would get the miss. + if len(b.stepping) == 0 { + b.unitTypes[key] = t + } + return t, t != "" +} + // dispatches reports whether a receiver of the declared type can hold an // instance of another type, so a call on it in a file of the language can // run the method of that type. A Go value of a named type that is not an diff --git a/projectgraph/golang.go b/projectgraph/golang.go index 1486b1c5..c6ccc4eb 100644 --- a/projectgraph/golang.go +++ b/projectgraph/golang.go @@ -23,6 +23,7 @@ var goProfile = profile{ narrowProviders: goNarrowProviders, surface: goSurface, symbol: goSymbol, + abstractReceiver: fileindex.UnresolvedInterfaceReceiver, dependencies: func(b *builder) ([]string, []modules.Problem) { return b.project.golang.Dependencies(), nil }, implicitInterfaces: true, typeConversions: true, diff --git a/projectgraph/graph.go b/projectgraph/graph.go index a08ad26a..4164d6a4 100644 --- a/projectgraph/graph.go +++ b/projectgraph/graph.go @@ -17,7 +17,7 @@ import ( // Version is the version of the link rules. Bump it when a rule that makes a // node, an edge, a root or an unresolved site changes, so a diff of two // reports shows the change. -const Version = "13" +const Version = "14" type NodeKind string diff --git a/projectgraph/link.go b/projectgraph/link.go index 032ea612..96ac1a0a 100644 --- a/projectgraph/link.go +++ b/projectgraph/link.go @@ -127,6 +127,9 @@ type builder struct { // walk of a unit reads now for the result type of an external key. reading map[string]bool stepping map[nameKey]bool + // unitTypes holds the type that a unit of dependency code gives for + // each external key, or "". + unitTypes map[string]string // groupMembers holds the members of each group of aliases by its first // alias, and nodesByParent the keys of the nodes by their parent. @@ -264,6 +267,7 @@ func newTables(indexes []*fileindex.Index, opts Options) *builder { defs: make(map[string]*fileindex.Definition, defs), reading: map[string]bool{}, stepping: map[nameKey]bool{}, + unitTypes: map[string]string{}, findingBases: map[string]bool{}, state: map[langcode.Code]any{}, units: map[string]*unit{}, diff --git a/projectgraph/packages.go b/projectgraph/packages.go index 3eb03a9d..8b4a1d3b 100644 --- a/projectgraph/packages.go +++ b/projectgraph/packages.go @@ -241,7 +241,13 @@ func (b *builder) recordOrigin(ix *fileindex.Index, key, module string, form mod // key. The origins of the key, with the names after their own, are the // origins of the new key. func (b *builder) deriveExternal(parent string, names []string) string { - key := b.external(fileindex.Join(parent, names...)) + return b.external(b.deriveKey(parent, names)) +} + +// deriveKey returns the key of the names under an external key, with the +// origins of the key and the names after their own, and makes no node. +func (b *builder) deriveKey(parent string, names []string) string { + key := fileindex.Join(parent, names...) for _, o := range b.origins[parent] { o.names = append(slices.Clone(o.names), names...) if !slices.ContainsFunc(b.origins[key], o.equal) { diff --git a/projectgraph/profile.go b/projectgraph/profile.go index c04fbb2c..d0f7b556 100644 --- a/projectgraph/profile.go +++ b/projectgraph/profile.go @@ -52,6 +52,18 @@ type profile struct { // it, as golang.org/x/net/html and Tokenizer. The profile of the family // holds it. symbol func(b *builder, sp *space, def string) (string, []string, bool) + // unitTypes marks a language with no subclass of a type of another + // crate, as Rust, so a declared type that a unit of dependency code + // gives narrows a call to the method of the type. + unitTypes bool + // sameTypeMethods name the methods that give a value of the type of + // the receiver, as clone of Rust, also when the type derives them and + // the code defines none. + sameTypeMethods map[string]bool + // abstractReceiver is the reason of the record of a call on a value of + // an interface or a trait that no type of the graph implements, as + // trait-receiver of Rust. + abstractReceiver fileindex.UnresolvedReason // dependencies returns the packages that the manifests of the project // declare for the ecosystem of the family, and the manifests that the // reader could not read. The profile of the family holds it. diff --git a/projectgraph/resolve.go b/projectgraph/resolve.go index cb83e2c7..fd7e7b6f 100644 --- a/projectgraph/resolve.go +++ b/projectgraph/resolve.go @@ -99,10 +99,10 @@ func (b *builder) linkCalls(ix *fileindex.Index) { } if r.fail != nil { reason := r.fail.reason - // A Go value of an interface type that no type of the + // A value of an interface or a trait that no type of the // graph implements holds a type of code elsewhere. - if reason == fileindex.UnresolvedNoDefinition && overrides == 0 && r.declared && sc.profile.implicitInterfaces && b.kindOf(r.class) == NodeInterface { - reason = fileindex.UnresolvedInterfaceReceiver + if reason == fileindex.UnresolvedNoDefinition && overrides == 0 && r.declared && sc.profile.abstractReceiver != "" && b.kindOf(r.class) == NodeInterface { + reason = sc.profile.abstractReceiver } b.addUnresolved(UnresolvedSite{ Caller: from, diff --git a/projectgraph/results.go b/projectgraph/results.go index d201fec2..c82d6d0b 100644 --- a/projectgraph/results.go +++ b/projectgraph/results.go @@ -8,32 +8,53 @@ import ( "github.com/safedep/code/projectgraph/modules" ) -// resultType returns the class or interface of the result at a position of -// a function, as a file of the unit of the importer sees it, "" for the -// project. A function of a package -// that the project imports is an external key, and the unit of dependency -// code that gives the package gives its definition. A function of a -// package that no unit gives has no known result, so a call on the result -// is uncertain with the reason no-dependency-source. -func (b *builder) resultType(unit, fn string, pos int) (string, fileindex.UnresolvedReason, bool) { +// result is the class or the interface of a result of a function, as a +// file of an importer names it, and the Option or the Result of std that +// holds a Rust result. +type result struct { + class string + wraps string +} + +// resultType returns the result at a position of a function, as a file of +// the unit of the importer sees it, "" for the project. A function of a +// package that the project imports is an external key, and the unit of +// dependency code that gives the package gives its definition. A function +// of a package that no unit gives has no known result, so a call on the +// result is uncertain with the reason no-dependency-source. +func (b *builder) resultType(unit, fn string, pos int) (result, fileindex.UnresolvedReason, bool) { defs := []string{fn} if b.defs[fn] == nil && b.g.IsExternal(fn) { var provided bool defs, provided = b.unitDefinitions(b.origins[fn]) if !provided { - return "", fileindex.UnresolvedNoDependencySource, false + return result{}, fileindex.UnresolvedNoDependencySource, false } } for _, d := range defs { - text := resultText(b.defs[d], pos) + def := b.defs[d] + text := resultText(def, pos) if text == "" { continue } if class, ok := b.resolveType(b.scopes[fileindex.Unit(d)], d, text); ok { - return b.typeFor(unit, class), "", true + return result{class: b.typeFor(unit, class), wraps: def.Wraps}, "", true } } - return "", fileindex.UnresolvedUnknownReceiver, false + return result{}, fileindex.UnresolvedUnknownReceiver, false +} + +// unwrap returns how many of the names after a call a wrapped result +// takes: one for unwrap or expect, which give the value inside, and none +// for a name that the value inside takes after a ?, which the index does +// not record. A method of the wrapper itself, as map of Option, gives no +// type, so it reports false. A result with no wrapper takes no name. Only +// a Rust result has a wrapper. +func unwrap(wraps string, next []string) (int, bool) { + if wraps == "" || len(next) == 0 { + return 0, true + } + return rustUnwrap(wraps, next[0]) } // resultText returns the type name of the result at a position of a @@ -61,7 +82,14 @@ func (b *builder) unitDefinitions(origins []origin) ([]string, bool) { for _, o := range origins { for _, pv := range b.providers(o) { provided = true - if pv.module == "" || len(o.names) == 0 { + switch { + case pv.module == "": + continue + case len(o.names) == 0: + // A Rust use can name the item itself, as use dep::Thing. + if b.defs[pv.module] != nil && !slices.Contains(defs, pv.module) { + defs = append(defs, pv.module) + } continue } k := nameKey{module: pv.module, name: fileindex.Join("", o.names...)} @@ -79,25 +107,35 @@ func (b *builder) unitDefinitions(origins []origin) ([]string, bool) { return defs, provided } -// externalResult returns the type of the result of the function or the -// method of the name under an external key, as golang.org//x//net//html//Tokenizer -// for NewTokenizer under golang.org//x//net//html. A unit of dependency -// code gives the function, and the importer of the key sees the type. -func (b *builder) externalResult(parent, name string) (string, bool) { +// externalResult returns the result of the function or the method of the +// name under an external key, as golang.org//x//net//html//Tokenizer for +// NewTokenizer under golang.org//x//net//html. A unit of dependency code +// gives the function, and the type is the external key of a class. A method +// of the same-type methods of the language, as clone in Rust, on a type of +// a unit keeps the type, also when the unit derives it and defines none. +func (b *builder) externalResult(parent, name string) (result, bool) { origins := derivedOrigins(b.origins[parent], name) if len(origins) == 0 { - return "", false + return result{}, false } defs, _ := b.unitDefinitions(origins) for _, d := range defs { if !b.isFunction(d) { continue } - if class, _, ok := b.resultType(origins[0].unit, d, 0); ok { - return class, true + // The walk reads an external key, so the code that names it is + // in another space than the unit, and the type is its key. + if res, _, ok := b.resultType("", d, 0); ok { + return res, true + } + } + if profileOf(origins[0].lang).sameTypeMethods[name] { + types, _ := b.unitDefinitions(b.origins[parent]) + if slices.ContainsFunc(types, b.isType) { + return result{class: parent}, true } } - return "", false + return result{}, false } // derivedOrigins returns the origins of the name under a key with the diff --git a/projectgraph/rust.go b/projectgraph/rust.go index 429a751b..995b8946 100644 --- a/projectgraph/rust.go +++ b/projectgraph/rust.go @@ -1,6 +1,7 @@ package projectgraph import ( + "slices" "strings" "github.com/safedep/code/fileindex" @@ -16,12 +17,19 @@ import ( // impl of a trait makes the trait a base. var rustProfile = profile{ code: langcode.Rust, - resolver: func(b *builder, _ *space) modules.Resolver { return rustResolver{b} }, + resolver: func(b *builder, sp *space) modules.Resolver { return rustResolver{b: b, sp: sp} }, units: (*builder).addRustUnits, + dependencyCode: true, aliases: (*builder).joinImplBlocks, link: (*builder).linkRust, scopeFallback: (*builder).rustScopeFallback, - dependencies: func(b *builder) ([]string, []modules.Problem) { return b.rustState().cargo.Dependencies(), nil }, + provide: rustProvide, + symbol: rustSymbol, + surface: rustSurface, + unitTypes: true, + sameTypeMethods: map[string]bool{"clone": true, "to_owned": true, "default": true, "from": true}, + abstractReceiver: fileindex.UnresolvedTraitReceiver, + dependencies: func(b *builder) ([]string, []modules.Problem) { return b.cargoOf(b.project).Dependencies(), nil }, instance: []string{"self"}, superModule: true, unitImports: true, @@ -69,15 +77,53 @@ var rustImplicit = map[string]bool{ "call_once": true, "serialize": true, "deserialize": true, } +// rustUnwraps are the methods of Option and Result that give the value +// inside, and rustWrapperMethods the other methods of each one. A ? +// gives the value inside too, and the index records no ?, so a name that +// neither table holds takes the value inside. +var ( + rustUnwraps = map[string]bool{"unwrap": true, "expect": true, "unwrap_unchecked": true} + rustWrapperMethods = map[string]map[string]bool{ + "Option": setOf("and", "and_then", "as_deref", "as_deref_mut", "as_mut", "as_ref", "cloned", "copied", "filter", + "flatten", "get_or_insert", "get_or_insert_with", "insert", "is_none", "is_some", "is_some_and", "iter", + "iter_mut", "map", "map_or", "map_or_else", "ok_or", "ok_or_else", "or", "or_else", "replace", "take", + "unwrap_or", "unwrap_or_default", "unwrap_or_else", "xor", "zip"), + "Result": setOf("and", "and_then", "as_deref", "as_deref_mut", "as_mut", "as_ref", "cloned", "copied", "err", + "expect_err", "flatten", "is_err", "is_ok", "is_ok_and", "iter", "iter_mut", "map", "map_err", "map_or", + "map_or_else", "ok", "or", "or_else", "unwrap_err", "unwrap_or", "unwrap_or_default", "unwrap_or_else"), + } +) + +func setOf(names ...string) map[string]bool { + out := make(map[string]bool, len(names)) + for _, n := range names { + out[n] = true + } + return out +} + +// rustUnwrap returns how many names a Rust result in the wrapper takes +// before the next name: one for an unwrap, none for a name of the value +// inside, and false for a method of the wrapper itself. +func rustUnwrap(wraps, next string) (int, bool) { + switch { + case rustUnwraps[next]: + return 1, true + case rustWrapperMethods[wraps][next]: + return 0, false + } + return 0, true +} + // rustRoots are the first names of the paths of the crates of the // toolchain, which no crate of the project defines. var rustRoots = map[string]bool{"std": true, "core": true, "alloc": true} -// rustState is the state of Rust in a build: the crates, the module path -// of each Rust file, and the paths that a resolution reads now, by the file -// that writes them. +// rustState is the state of Rust in a build: the crates of each space, the +// module path of each Rust file, and the paths that a resolution reads now, +// by the file that writes them. type rustState struct { - cargo *modules.Cargo + cargo map[*space]*modules.Cargo modules map[string]string resolving map[[2]string]bool } @@ -86,12 +132,25 @@ func (b *builder) rustState() *rustState { return langState[rustState](b, langcode.Rust) } +// cargoOf returns the crates of the Cargo.toml files of a space. +func (b *builder) cargoOf(sp *space) *modules.Cargo { + s := b.rustState() + if s.cargo == nil { + s.cargo = map[*space]*modules.Cargo{} + } + c, ok := s.cargo[sp] + if !ok { + c = modules.NewCargo(sp.files, sp.root) + s.cargo[sp] = c + b.addProblems(sp, c.Problems()) + } + return c +} + // addRustUnits makes each Rust file the unit of its module, in the table of // the module paths. func (b *builder) addRustUnits() { s := b.rustState() - s.cargo = modules.NewCargo(b.project.files, b.opts.Root) - b.problems = append(b.problems, s.cargo.Problems()...) s.modules = map[string]string{} s.resolving = map[[2]string]bool{} files := map[string][]string{} @@ -115,15 +174,25 @@ func (b *builder) addRustUnits() { // with no Cargo.toml. src/lib.rs and src/main.rs are the root of the crate. // A file of src/bin, tests, examples or benches, and build.rs, is the root // of a crate of its own, which no path of another file names. +// +// The root of a crate of a unit of dependency code is the unit, as +// cargo:smallvec@1.13.2::alloc for src/alloc.rs of smallvec. So the +// modules of two versions of one crate, or of a crate of the project with +// the name of a dependency, stay apart. func (b *builder) rustModule(p string) string { - crate, ok := b.rustState().cargo.Crate(p) - name, rel := "crate", p + sp := b.spaceOf(p) + inner := sp.inner(p) + crate, ok := b.cargoOf(sp).Crate(inner) + name, rel := "crate", inner if ok { name = crate.Name if crate.Dir != "" { - rel = strings.TrimPrefix(p, crate.Dir+"/") + rel = strings.TrimPrefix(inner, crate.Dir+"/") } } + if sp.unit != "" { + name = sp.unit + } src, inSrc := strings.CutPrefix(rel, "src/") first, _, _ := strings.Cut(rel, "/") switch { @@ -164,12 +233,102 @@ func (b *builder) rustPaths(module, written string) []string { return []string{joinPath(module, parts)} } out := []string{joinPath(module, parts)} - if b.rustState().cargo.Has(parts[0]) { + switch sp := b.rustSpaceOf(module); { + case sp.unit != "" && b.rustCrateName(sp) == parts[0]: + out = append(out, joinPath(sp.unit, parts[1:])) + case sp.unit == "" && b.cargoOf(sp).Has(parts[0]): out = append(out, written) } return out } +// rustSpaceOf returns the space of a module path: the unit of its root, or +// the project. +func (b *builder) rustSpaceOf(module string) *space { + root, _, _ := strings.Cut(module, "::") + if sp := b.spaces[root]; sp != nil { + return sp + } + return b.project +} + +// rustCrateName returns the name that a path of another crate writes for +// the crate of a unit of dependency code, as tokio_postgres for the package +// tokio-postgres: the name of its root Cargo.toml, or else the name of its +// package. +func (b *builder) rustCrateName(sp *space) string { + if crate, ok := b.cargoOf(sp).Crate("src/lib.rs"); ok && crate.Dir == "" { + return crate.Name + } + return strings.ReplaceAll(sp.pkg.Name, "-", "_") +} + +// rustProvide returns the item of a crate of a unit of dependency code that +// a path of another crate names, as the struct SmallVec of the unit +// cargo:smallvec@1.13.2 for smallvec::SmallVec. +func rustProvide(b *builder, sp *space, module string, _ modules.Form) []provision { + parts := strings.Split(strings.TrimPrefix(module, "::"), "::") + if sp.pkg.Ecosystem != "cargo" || b.rustCrateName(sp) != parts[0] { + return nil + } + if key, ok := b.resolveQualified(langcode.Rust, joinPath(sp.unit, parts[1:])); ok { + return []provision{{sp: sp, module: key, via: "crate"}} + } + return []provision{{sp: sp}} +} + +// rustSurface adds each pub item of the crate of a unit and each pub +// method of its types, under the path that another crate writes for it. +func rustSurface(s *surfacer) { + b, sp := s.b, s.sp + if sp.pkg.Ecosystem != "cargo" { + return + } + for _, p := range b.paths { + ix := b.byPath[p] + if ix == nil || ix.Language != langcode.Rust || fileindex.PackageOf(p) != sp.unit { + continue + } + for _, def := range ix.Definitions { + segs := fileindex.Segments(def.Key) + if !def.Exported || len(segs) == 0 || len(segs) > 2 || len(segs) == 2 && def.Kind != fileindex.DefMethod { + continue + } + if module, names, ok := rustSymbol(b, sp, def.Key); ok { + s.add(langcode.Rust, module, modules.FormImport, names...) + } + } + } +} + +// rustSymbol returns the path of the module of a Rust definition of a unit +// as another crate writes it and the names of the definition, as smallvec +// and SmallVec, insert. A type of a private module that the root of the +// crate gives on with pub use gets the shortest path, as a user of the +// crate writes it. +func rustSymbol(b *builder, sp *space, def string) (string, []string, bool) { + module, ok := b.rustState().modules[fileindex.Unit(def)] + if !ok { + return "", nil, false + } + var segs []string + if _, rest, ok := strings.Cut(module, "::"); ok { + segs = strings.Split(rest, "::") + } + names := fileindex.Segments(def) + if len(names) == 0 { + return "", nil, false + } + top := fileindex.Join(fileindex.Unit(def), names[0]) + for i := range segs { + if key, ok := b.resolveQualified(langcode.Rust, joinPath(sp.unit, append(slices.Clone(segs[:i]), names[0]))); ok && key == top { + segs = segs[:i] + break + } + } + return joinPath(b.rustCrateName(sp), segs), names, true +} + func joinPath(module string, parts []string) string { return strings.Join(append([]string{module}, parts...), "::") } @@ -200,8 +359,11 @@ func (b *builder) resolveRust(importer, written string) (string, bool) { } // rustResolver resolves a use, which names a module, a type or a function -// with its path. -type rustResolver struct{ b *builder } +// with its path, in one space. +type rustResolver struct { + b *builder + sp *space +} func (r rustResolver) Module(importer, spec string) (string, string, bool) { if key, ok := r.b.resolveRust(importer, spec); ok { @@ -221,13 +383,14 @@ func (r rustResolver) FirstParty(spec string) bool { case rustRoots[first]: return false } - return r.b.rustState().cargo.Has(first) || r.b.project.files.HasName(first) + return r.b.cargoOf(r.sp).Has(first) || r.sp.files.HasName(first) } // rustScopeFallback resolves a path that no use and no item of the module // binds: a path of the crate, as crate::store::save or store::save for a // child module, or else a path of another crate, as serde_json::to_string, -// whose external key the edge gets. A single name stays a record, because +// whose external key the edge gets. The crate is the origin of the key, so a +// unit of dependency code that gives the crate links it. A single name stays a record, because // a local closure can bind it. func (b *builder) rustScopeFallback(sc *scope, _, first string, rest []string, target string) []resolution { written := strings.Join(append([]string{first}, rest...), "::") @@ -235,7 +398,7 @@ func (b *builder) rustScopeFallback(sc *scope, _, first string, rest []string, t return []resolution{b.walkNamespace(key)} } if len(rest) > 0 { - return []resolution{b.externalResolution(target)} + return []resolution{b.importedWalk(sc.ix, target, first, modules.FormImport, rest)} } return nil } diff --git a/projectgraph/testdata/javascript/barrel-chain/graph.golden.json b/projectgraph/testdata/javascript/barrel-chain/graph.golden.json index 7aa9f7d9..491e3dd3 100644 --- a/projectgraph/testdata/javascript/barrel-chain/graph.golden.json +++ b/projectgraph/testdata/javascript/barrel-chain/graph.golden.json @@ -174,7 +174,7 @@ "src/main.js": "indexed" }, "stats": { - "version": "13", + "version": "14", "files": 4, "nodes": 7, "edges": 6, diff --git a/projectgraph/testdata/javascript/barrel-chain/indexes.json b/projectgraph/testdata/javascript/barrel-chain/indexes.json index 75d25f01..d1cffad8 100644 --- a/projectgraph/testdata/javascript/barrel-chain/indexes.json +++ b/projectgraph/testdata/javascript/barrel-chain/indexes.json @@ -1,22 +1,22 @@ [ { - "version": "13", "path": "src/main.js", "language": "javascript", + "version": "14", "path": "src/main.js", "language": "javascript", "imports": [{"line": 1, "module": "./api", "item": "parse", "alias": "parse", "kind": "static", "namespace": ".//api//parse"}], "definitions": [{"key": "src/main.js", "kind": "module", "name": "main", "start_line": 1, "end_line": 2, "exported": true}], "calls": [{"caller": "src/main.js", "line": 2, "column": 1, "callee": "parse", "targets": [".//api//parse"], "kind": "call"}] }, { - "version": "13", "path": "src/api/index.js", "language": "javascript", + "version": "14", "path": "src/api/index.js", "language": "javascript", "exports": [{"name": "parse", "target": "", "from": "../core", "item": "parse", "line": 1}], "definitions": [{"key": "src/api/index.js", "kind": "module", "name": "index", "start_line": 1, "end_line": 1, "exported": true}] }, { - "version": "13", "path": "src/core/index.js", "language": "javascript", + "version": "14", "path": "src/core/index.js", "language": "javascript", "exports": [{"name": "parse", "target": "", "from": "./parse", "item": "doParse", "line": 1}], "definitions": [{"key": "src/core/index.js", "kind": "module", "name": "index", "start_line": 1, "end_line": 1, "exported": true}] }, { - "version": "13", "path": "src/core/parse.js", "language": "javascript", + "version": "14", "path": "src/core/parse.js", "language": "javascript", "exports": [{"name": "doParse", "target": "src/core/parse.js//doParse", "line": 1}], "definitions": [ {"key": "src/core/parse.js", "kind": "module", "name": "parse", "start_line": 1, "end_line": 3, "exported": true}, diff --git a/projectgraph/testdata/javascript/commonjs-require/graph.golden.json b/projectgraph/testdata/javascript/commonjs-require/graph.golden.json index df340c72..8d0ae176 100644 --- a/projectgraph/testdata/javascript/commonjs-require/graph.golden.json +++ b/projectgraph/testdata/javascript/commonjs-require/graph.golden.json @@ -138,7 +138,7 @@ "main.js": "indexed" }, "stats": { - "version": "13", + "version": "14", "files": 3, "nodes": 5, "edges": 4, diff --git a/projectgraph/testdata/javascript/commonjs-require/indexes.json b/projectgraph/testdata/javascript/commonjs-require/indexes.json index 984ac4c2..cda79406 100644 --- a/projectgraph/testdata/javascript/commonjs-require/indexes.json +++ b/projectgraph/testdata/javascript/commonjs-require/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "13", "path": "main.js", "language": "javascript", + "version": "14", "path": "main.js", "language": "javascript", "imports": [ {"line": 1, "module": "./lib", "alias": "utils", "kind": "static", "namespace": ".//lib"}, {"line": 2, "module": "./helper", "item": "helper", "alias": "helper", "kind": "static", "namespace": ".//helper//helper"} @@ -12,7 +12,7 @@ ] }, { - "version": "13", "path": "lib/index.js", "language": "javascript", + "version": "14", "path": "lib/index.js", "language": "javascript", "exports": [{"name": "run", "target": "lib/index.js//run", "line": 3}], "definitions": [ {"key": "lib/index.js", "kind": "module", "name": "index", "start_line": 1, "end_line": 3, "exported": true}, @@ -20,7 +20,7 @@ ] }, { - "version": "13", "path": "helper.js", "language": "javascript", + "version": "14", "path": "helper.js", "language": "javascript", "exports": [{"name": "helper", "target": "helper.js//helper", "line": 3}], "definitions": [ {"key": "helper.js", "kind": "module", "name": "helper", "start_line": 1, "end_line": 3, "exported": true}, diff --git a/projectgraph/testdata/javascript/default-import/graph.golden.json b/projectgraph/testdata/javascript/default-import/graph.golden.json index 9d90e497..ed8eeee9 100644 --- a/projectgraph/testdata/javascript/default-import/graph.golden.json +++ b/projectgraph/testdata/javascript/default-import/graph.golden.json @@ -138,7 +138,7 @@ "make.js": "indexed" }, "stats": { - "version": "13", + "version": "14", "files": 3, "nodes": 5, "edges": 4, diff --git a/projectgraph/testdata/javascript/default-import/indexes.json b/projectgraph/testdata/javascript/default-import/indexes.json index 2c2028e5..fcd832a1 100644 --- a/projectgraph/testdata/javascript/default-import/indexes.json +++ b/projectgraph/testdata/javascript/default-import/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "13", "path": "main.js", "language": "javascript", + "version": "14", "path": "main.js", "language": "javascript", "imports": [ {"line": 1, "module": "./log", "alias": "log", "kind": "static", "namespace": ".//log"}, {"line": 2, "module": "./make", "alias": "make", "kind": "static", "namespace": ".//make"} @@ -12,7 +12,7 @@ ] }, { - "version": "13", "path": "log.js", "language": "javascript", + "version": "14", "path": "log.js", "language": "javascript", "exports": [{"name": "default", "target": "log.js//log", "line": 1}], "definitions": [ {"key": "log.js", "kind": "module", "name": "log", "start_line": 1, "end_line": 1, "exported": true}, @@ -20,7 +20,7 @@ ] }, { - "version": "13", "path": "make.js", "language": "javascript", + "version": "14", "path": "make.js", "language": "javascript", "exports": [{"name": "default", "target": "make.js//make", "line": 1}], "definitions": [ {"key": "make.js", "kind": "module", "name": "make", "start_line": 1, "end_line": 1, "exported": true}, diff --git a/projectgraph/testdata/javascript/esm-named-import/graph.golden.json b/projectgraph/testdata/javascript/esm-named-import/graph.golden.json index 6b3b5b33..d067bd0c 100644 --- a/projectgraph/testdata/javascript/esm-named-import/graph.golden.json +++ b/projectgraph/testdata/javascript/esm-named-import/graph.golden.json @@ -166,7 +166,7 @@ "src/util.js": "indexed" }, "stats": { - "version": "13", + "version": "14", "files": 3, "nodes": 6, "edges": 5, diff --git a/projectgraph/testdata/javascript/esm-named-import/indexes.json b/projectgraph/testdata/javascript/esm-named-import/indexes.json index 1a9c0a0c..60c79a4b 100644 --- a/projectgraph/testdata/javascript/esm-named-import/indexes.json +++ b/projectgraph/testdata/javascript/esm-named-import/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "13", "path": "src/main.js", "language": "javascript", + "version": "14", "path": "src/main.js", "language": "javascript", "imports": [ {"line": 1, "module": "./parser.js", "item": "parse", "alias": "parse", "kind": "static", "namespace": ".//parser.js//parse"}, {"line": 2, "module": "./util", "item": "fmt", "alias": "fmt", "kind": "static", "namespace": ".//util//fmt"}, @@ -14,7 +14,7 @@ "unresolved": [{"caller": "src/main.js", "line": 5, "column": 1, "text": "import", "reason": "dynamic-import"}] }, { - "version": "13", "path": "src/parser.js", "language": "javascript", + "version": "14", "path": "src/parser.js", "language": "javascript", "imports": [{"line": 1, "module": "js-yaml", "alias": "yaml", "kind": "static", "namespace": "js-yaml"}], "exports": [{"name": "parse", "target": "src/parser.js//parse", "line": 2}], "definitions": [ @@ -24,7 +24,7 @@ "calls": [{"caller": "src/parser.js//parse", "line": 3, "column": 10, "callee": "yaml.load", "targets": ["js-yaml//load"], "receiver": "yaml", "method": "load", "kind": "call"}] }, { - "version": "13", "path": "src/util.js", "language": "javascript", + "version": "14", "path": "src/util.js", "language": "javascript", "exports": [{"name": "fmt", "target": "src/util.js//fmt", "line": 1}], "definitions": [ {"key": "src/util.js", "kind": "module", "name": "util", "start_line": 1, "end_line": 1, "exported": true}, diff --git a/projectgraph/testdata/javascript/export-star/graph.golden.json b/projectgraph/testdata/javascript/export-star/graph.golden.json index 91054813..fb622523 100644 --- a/projectgraph/testdata/javascript/export-star/graph.golden.json +++ b/projectgraph/testdata/javascript/export-star/graph.golden.json @@ -276,7 +276,7 @@ "main.js": "indexed" }, "stats": { - "version": "13", + "version": "14", "files": 5, "nodes": 11, "edges": 10, diff --git a/projectgraph/testdata/javascript/export-star/indexes.json b/projectgraph/testdata/javascript/export-star/indexes.json index 47e8dd47..0ede83ab 100644 --- a/projectgraph/testdata/javascript/export-star/indexes.json +++ b/projectgraph/testdata/javascript/export-star/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "13", "path": "main.js", "language": "javascript", + "version": "14", "path": "main.js", "language": "javascript", "imports": [ {"line": 1, "module": "./all", "item": "a", "alias": "a", "kind": "static", "namespace": ".//all//a"}, {"line": 1, "module": "./all", "item": "b", "alias": "b", "kind": "static", "namespace": ".//all//b"}, @@ -14,7 +14,7 @@ ] }, { - "version": "13", "path": "all.js", "language": "javascript", + "version": "14", "path": "all.js", "language": "javascript", "exports": [ {"name": "*", "target": "", "from": "./a", "item": "*", "line": 1}, {"name": "*", "target": "", "from": "./b", "item": "*", "line": 2}, @@ -23,7 +23,7 @@ "definitions": [{"key": "all.js", "kind": "module", "name": "all", "start_line": 1, "end_line": 3, "exported": true}] }, { - "version": "13", "path": "a.js", "language": "javascript", + "version": "14", "path": "a.js", "language": "javascript", "exports": [{"name": "a", "target": "a.js//a", "line": 1}], "definitions": [ {"key": "a.js", "kind": "module", "name": "a", "start_line": 1, "end_line": 1, "exported": true}, @@ -31,7 +31,7 @@ ] }, { - "version": "13", "path": "b.js", "language": "javascript", + "version": "14", "path": "b.js", "language": "javascript", "exports": [{"name": "b", "target": "b.js//b", "line": 1}], "definitions": [ {"key": "b.js", "kind": "module", "name": "b", "start_line": 1, "end_line": 1, "exported": true}, @@ -39,7 +39,7 @@ ] }, { - "version": "13", "path": "c.js", "language": "javascript", + "version": "14", "path": "c.js", "language": "javascript", "exports": [{"name": "c", "target": "c.js//c", "line": 1}], "definitions": [ {"key": "c.js", "kind": "module", "name": "c", "start_line": 1, "end_line": 1, "exported": true}, diff --git a/projectgraph/testdata/javascript/exports-conditions/graph.golden.json b/projectgraph/testdata/javascript/exports-conditions/graph.golden.json index 633ebc58..9c2f46c9 100644 --- a/projectgraph/testdata/javascript/exports-conditions/graph.golden.json +++ b/projectgraph/testdata/javascript/exports-conditions/graph.golden.json @@ -144,7 +144,7 @@ "lib/src/feature/y.js": "indexed" }, "stats": { - "version": "13", + "version": "14", "files": 4, "nodes": 7, "edges": 4, diff --git a/projectgraph/testdata/javascript/exports-conditions/indexes.json b/projectgraph/testdata/javascript/exports-conditions/indexes.json index 534682a6..c0aefd37 100644 --- a/projectgraph/testdata/javascript/exports-conditions/indexes.json +++ b/projectgraph/testdata/javascript/exports-conditions/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "13", "path": "app/main.mjs", "language": "javascript", + "version": "14", "path": "app/main.mjs", "language": "javascript", "imports": [ {"line": 1, "module": "@acme/lib", "item": "x", "alias": "x", "kind": "static", "namespace": "@acme//lib//x"}, {"line": 2, "module": "@acme/lib/feature/y", "item": "y", "alias": "y", "kind": "static", "namespace": "@acme//lib//feature//y//y"} @@ -12,7 +12,7 @@ ] }, { - "version": "13", "path": "lib/esm/index.mjs", "language": "javascript", + "version": "14", "path": "lib/esm/index.mjs", "language": "javascript", "exports": [{"name": "x", "target": "lib/esm/index.mjs//x", "line": 1}], "definitions": [ {"key": "lib/esm/index.mjs", "kind": "module", "name": "index", "start_line": 1, "end_line": 1, "exported": true}, @@ -20,7 +20,7 @@ ] }, { - "version": "13", "path": "lib/cjs/index.cjs", "language": "javascript", + "version": "14", "path": "lib/cjs/index.cjs", "language": "javascript", "exports": [{"name": "x", "target": "lib/cjs/index.cjs//x", "line": 1}], "definitions": [ {"key": "lib/cjs/index.cjs", "kind": "module", "name": "index", "start_line": 1, "end_line": 1, "exported": true}, @@ -28,7 +28,7 @@ ] }, { - "version": "13", "path": "lib/src/feature/y.js", "language": "javascript", + "version": "14", "path": "lib/src/feature/y.js", "language": "javascript", "exports": [{"name": "y", "target": "lib/src/feature/y.js//y", "line": 1}], "definitions": [ {"key": "lib/src/feature/y.js", "kind": "module", "name": "y", "start_line": 1, "end_line": 1, "exported": true}, diff --git a/projectgraph/testdata/javascript/unknown-receiver/graph.golden.json b/projectgraph/testdata/javascript/unknown-receiver/graph.golden.json index 4e9329bc..211643eb 100644 --- a/projectgraph/testdata/javascript/unknown-receiver/graph.golden.json +++ b/projectgraph/testdata/javascript/unknown-receiver/graph.golden.json @@ -124,7 +124,7 @@ "main.js": "indexed" }, "stats": { - "version": "13", + "version": "14", "files": 1, "nodes": 5, "edges": 3, diff --git a/projectgraph/testdata/javascript/unknown-receiver/indexes.json b/projectgraph/testdata/javascript/unknown-receiver/indexes.json index bc462192..c4414fa2 100644 --- a/projectgraph/testdata/javascript/unknown-receiver/indexes.json +++ b/projectgraph/testdata/javascript/unknown-receiver/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "13", + "version": "14", "path": "main.js", "language": "javascript", "imports": [ diff --git a/projectgraph/testdata/javascript/workspaces/graph.golden.json b/projectgraph/testdata/javascript/workspaces/graph.golden.json index c0b79688..cc6054a2 100644 --- a/projectgraph/testdata/javascript/workspaces/graph.golden.json +++ b/projectgraph/testdata/javascript/workspaces/graph.golden.json @@ -127,7 +127,7 @@ "packages/core/util.js": "indexed" }, "stats": { - "version": "13", + "version": "14", "files": 3, "nodes": 6, "edges": 5, diff --git a/projectgraph/testdata/javascript/workspaces/indexes.json b/projectgraph/testdata/javascript/workspaces/indexes.json index f534f937..65319d4b 100644 --- a/projectgraph/testdata/javascript/workspaces/indexes.json +++ b/projectgraph/testdata/javascript/workspaces/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "13", "path": "packages/app/src/main.js", "language": "javascript", + "version": "14", "path": "packages/app/src/main.js", "language": "javascript", "imports": [ {"line": 1, "module": "@acme/core", "item": "start", "alias": "start", "kind": "static", "namespace": "@acme//core//start"}, {"line": 2, "module": "@acme/core/util", "item": "tidy", "alias": "tidy", "kind": "static", "namespace": "@acme//core//util//tidy"}, @@ -14,7 +14,7 @@ ] }, { - "version": "13", "path": "packages/core/lib/index.js", "language": "javascript", + "version": "14", "path": "packages/core/lib/index.js", "language": "javascript", "exports": [{"name": "start", "target": "packages/core/lib/index.js//start", "line": 1}], "definitions": [ {"key": "packages/core/lib/index.js", "kind": "module", "name": "index", "start_line": 1, "end_line": 1, "exported": true}, @@ -22,7 +22,7 @@ ] }, { - "version": "13", "path": "packages/core/util.js", "language": "javascript", + "version": "14", "path": "packages/core/util.js", "language": "javascript", "exports": [{"name": "tidy", "target": "packages/core/util.js//tidy", "line": 1}], "definitions": [ {"key": "packages/core/util.js", "kind": "module", "name": "util", "start_line": 1, "end_line": 1, "exported": true}, diff --git a/projectgraph/testdata/python/all-hides/graph.golden.json b/projectgraph/testdata/python/all-hides/graph.golden.json index f763eb3e..bb4f422e 100644 --- a/projectgraph/testdata/python/all-hides/graph.golden.json +++ b/projectgraph/testdata/python/all-hides/graph.golden.json @@ -174,7 +174,7 @@ "pkg/a.py": "indexed" }, "stats": { - "version": "13", + "version": "14", "files": 3, "nodes": 7, "edges": 6, diff --git a/projectgraph/testdata/python/all-hides/indexes.json b/projectgraph/testdata/python/all-hides/indexes.json index 5f3e1202..bf36a029 100644 --- a/projectgraph/testdata/python/all-hides/indexes.json +++ b/projectgraph/testdata/python/all-hides/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "13", "path": "main.py", "language": "python", + "version": "14", "path": "main.py", "language": "python", "imports": [ {"line": 1, "module": "pkg", "item": "f", "alias": "f", "kind": "static", "namespace": "pkg//f"}, {"line": 1, "module": "pkg", "item": "g", "alias": "g", "kind": "static", "namespace": "pkg//g"} @@ -12,7 +12,7 @@ ] }, { - "version": "13", "path": "pkg/__init__.py", "language": "python", + "version": "14", "path": "pkg/__init__.py", "language": "python", "imports": [ {"line": 1, "module": ".a", "item": "f", "alias": "f", "kind": "static", "namespace": "//a//f"}, {"line": 1, "module": ".a", "item": "g", "alias": "g", "kind": "static", "namespace": "//a//g"} @@ -21,7 +21,7 @@ "definitions": [{"key": "pkg/__init__.py", "kind": "module", "name": "__init__", "start_line": 1, "end_line": 2, "exported": true}] }, { - "version": "13", "path": "pkg/a.py", "language": "python", + "version": "14", "path": "pkg/a.py", "language": "python", "definitions": [ {"key": "pkg/a.py", "kind": "module", "name": "a", "start_line": 1, "end_line": 4, "exported": true}, {"key": "pkg/a.py//f", "kind": "function", "name": "f", "start_line": 1, "end_line": 2, "exported": true}, diff --git a/projectgraph/testdata/python/assignment/graph.golden.json b/projectgraph/testdata/python/assignment/graph.golden.json index 3c9f53e8..dca6a39c 100644 --- a/projectgraph/testdata/python/assignment/graph.golden.json +++ b/projectgraph/testdata/python/assignment/graph.golden.json @@ -70,7 +70,7 @@ "main.py": "indexed" }, "stats": { - "version": "13", + "version": "14", "files": 1, "nodes": 3, "edges": 2, diff --git a/projectgraph/testdata/python/assignment/indexes.json b/projectgraph/testdata/python/assignment/indexes.json index 75165aa4..4a129982 100644 --- a/projectgraph/testdata/python/assignment/indexes.json +++ b/projectgraph/testdata/python/assignment/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "13", "path": "main.py", "language": "python", + "version": "14", "path": "main.py", "language": "python", "imports": [{"line": 1, "module": "yaml", "alias": "yaml", "kind": "static", "namespace": "yaml"}], "definitions": [ {"key": "main.py", "kind": "module", "name": "main", "start_line": 1, "end_line": 7, "exported": true}, diff --git a/projectgraph/testdata/python/class-body/graph.golden.json b/projectgraph/testdata/python/class-body/graph.golden.json index bfe7cf9c..3319884e 100644 --- a/projectgraph/testdata/python/class-body/graph.golden.json +++ b/projectgraph/testdata/python/class-body/graph.golden.json @@ -224,7 +224,7 @@ "config.py": "indexed" }, "stats": { - "version": "13", + "version": "14", "files": 1, "nodes": 12, "edges": 7, diff --git a/projectgraph/testdata/python/class-body/indexes.json b/projectgraph/testdata/python/class-body/indexes.json index c9f20b9f..aa75873e 100644 --- a/projectgraph/testdata/python/class-body/indexes.json +++ b/projectgraph/testdata/python/class-body/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "13", + "version": "14", "path": "config.py", "language": "python", "imports": [ diff --git a/projectgraph/testdata/python/decorators/graph.golden.json b/projectgraph/testdata/python/decorators/graph.golden.json index 0a6e818f..2e4026e6 100644 --- a/projectgraph/testdata/python/decorators/graph.golden.json +++ b/projectgraph/testdata/python/decorators/graph.golden.json @@ -194,7 +194,7 @@ "app.py": "indexed" }, "stats": { - "version": "13", + "version": "14", "files": 1, "nodes": 9, "edges": 5, diff --git a/projectgraph/testdata/python/decorators/indexes.json b/projectgraph/testdata/python/decorators/indexes.json index 1cf6be87..b5f50d62 100644 --- a/projectgraph/testdata/python/decorators/indexes.json +++ b/projectgraph/testdata/python/decorators/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "13", + "version": "14", "path": "app.py", "language": "python", "imports": [ diff --git a/projectgraph/testdata/python/external/graph.golden.json b/projectgraph/testdata/python/external/graph.golden.json index 18683074..324dbf61 100644 --- a/projectgraph/testdata/python/external/graph.golden.json +++ b/projectgraph/testdata/python/external/graph.golden.json @@ -108,7 +108,7 @@ "main.py": "indexed" }, "stats": { - "version": "13", + "version": "14", "files": 1, "nodes": 5, "edges": 3, diff --git a/projectgraph/testdata/python/external/indexes.json b/projectgraph/testdata/python/external/indexes.json index a2b4bd88..8abec365 100644 --- a/projectgraph/testdata/python/external/indexes.json +++ b/projectgraph/testdata/python/external/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "13", "path": "main.py", "language": "python", + "version": "14", "path": "main.py", "language": "python", "imports": [ {"line": 1, "module": "requests", "alias": "requests", "kind": "static", "namespace": "requests"}, {"line": 2, "module": "yaml", "item": "load", "alias": "yl", "kind": "static", "namespace": "yaml//load"}, diff --git a/projectgraph/testdata/python/local-receiver/graph.golden.json b/projectgraph/testdata/python/local-receiver/graph.golden.json index e0b12b9d..eca478a1 100644 --- a/projectgraph/testdata/python/local-receiver/graph.golden.json +++ b/projectgraph/testdata/python/local-receiver/graph.golden.json @@ -145,7 +145,7 @@ "main.py": "indexed" }, "stats": { - "version": "13", + "version": "14", "files": 1, "nodes": 5, "edges": 3, diff --git a/projectgraph/testdata/python/local-receiver/indexes.json b/projectgraph/testdata/python/local-receiver/indexes.json index adc5e30e..09ba712c 100644 --- a/projectgraph/testdata/python/local-receiver/indexes.json +++ b/projectgraph/testdata/python/local-receiver/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "13", + "version": "14", "path": "main.py", "language": "python", "imports": [ diff --git a/projectgraph/testdata/python/member-call/graph.golden.json b/projectgraph/testdata/python/member-call/graph.golden.json index b4e08c6c..1c3a4bf0 100644 --- a/projectgraph/testdata/python/member-call/graph.golden.json +++ b/projectgraph/testdata/python/member-call/graph.golden.json @@ -258,7 +258,7 @@ "parser.py": "indexed" }, "stats": { - "version": "13", + "version": "14", "files": 3, "nodes": 9, "edges": 10, diff --git a/projectgraph/testdata/python/member-call/indexes.json b/projectgraph/testdata/python/member-call/indexes.json index 3b73b713..0d0fd90f 100644 --- a/projectgraph/testdata/python/member-call/indexes.json +++ b/projectgraph/testdata/python/member-call/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "13", "path": "main.py", "language": "python", + "version": "14", "path": "main.py", "language": "python", "imports": [{"line": 1, "module": "parser", "item": "Parser", "alias": "Parser", "kind": "static", "namespace": "parser//Parser"}], "definitions": [{"key": "main.py", "kind": "module", "name": "main", "start_line": 1, "end_line": 4, "exported": true}], "calls": [ @@ -10,7 +10,7 @@ ] }, { - "version": "13", "path": "parser.py", "language": "python", + "version": "14", "path": "parser.py", "language": "python", "imports": [{"line": 1, "module": "base", "item": "Base", "alias": "Base", "kind": "static", "namespace": "base//Base"}], "definitions": [ {"key": "parser.py", "kind": "module", "name": "parser", "start_line": 1, "end_line": 7, "exported": true}, @@ -22,7 +22,7 @@ "calls": [{"caller": "parser.py//Parser//parse", "line": 6, "column": 9, "callee": "self.helper", "targets": ["parser.py//Parser//helper"], "receiver": "self", "method": "helper", "kind": "call"}] }, { - "version": "13", "path": "base.py", "language": "python", + "version": "14", "path": "base.py", "language": "python", "imports": [{"line": 1, "module": "io", "alias": "io", "kind": "static", "namespace": "io"}], "definitions": [ {"key": "base.py", "kind": "module", "name": "base", "start_line": 1, "end_line": 4, "exported": true}, diff --git a/projectgraph/testdata/python/missing-file/graph.golden.json b/projectgraph/testdata/python/missing-file/graph.golden.json index 5ea99fbd..3edc0d2a 100644 --- a/projectgraph/testdata/python/missing-file/graph.golden.json +++ b/projectgraph/testdata/python/missing-file/graph.golden.json @@ -55,7 +55,7 @@ "main.py": "cached" }, "stats": { - "version": "13", + "version": "14", "files": 2, "nodes": 2, "edges": 2, diff --git a/projectgraph/testdata/python/missing-file/indexes.json b/projectgraph/testdata/python/missing-file/indexes.json index e27a4cf5..b42f5396 100644 --- a/projectgraph/testdata/python/missing-file/indexes.json +++ b/projectgraph/testdata/python/missing-file/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "13", "path": "main.py", "language": "python", + "version": "14", "path": "main.py", "language": "python", "imports": [{"line": 1, "module": "handlers", "item": "parse", "alias": "parse", "kind": "static", "namespace": "handlers//parse"}], "definitions": [{"key": "main.py", "kind": "module", "name": "main", "start_line": 1, "end_line": 2, "exported": true}], "calls": [{"caller": "main.py", "line": 2, "column": 1, "callee": "parse", "targets": ["handlers//parse"], "kind": "call"}] diff --git a/projectgraph/testdata/python/package-init/graph.golden.json b/projectgraph/testdata/python/package-init/graph.golden.json index 04bd929e..989d63d1 100644 --- a/projectgraph/testdata/python/package-init/graph.golden.json +++ b/projectgraph/testdata/python/package-init/graph.golden.json @@ -144,7 +144,7 @@ "pkg/loader.py": "indexed" }, "stats": { - "version": "13", + "version": "14", "files": 3, "nodes": 6, "edges": 5, diff --git a/projectgraph/testdata/python/package-init/indexes.json b/projectgraph/testdata/python/package-init/indexes.json index e840dd3c..6e275227 100644 --- a/projectgraph/testdata/python/package-init/indexes.json +++ b/projectgraph/testdata/python/package-init/indexes.json @@ -1,17 +1,17 @@ [ { - "version": "13", "path": "main.py", "language": "python", + "version": "14", "path": "main.py", "language": "python", "imports": [{"line": 1, "module": "pkg", "item": "load", "alias": "load", "kind": "static", "namespace": "pkg//load"}], "definitions": [{"key": "main.py", "kind": "module", "name": "main", "start_line": 1, "end_line": 2, "exported": true}], "calls": [{"caller": "main.py", "line": 2, "column": 1, "callee": "load", "targets": ["pkg//load"], "kind": "call"}] }, { - "version": "13", "path": "pkg/__init__.py", "language": "python", + "version": "14", "path": "pkg/__init__.py", "language": "python", "imports": [{"line": 1, "module": ".loader", "item": "load", "alias": "load", "kind": "static", "namespace": "//loader//load"}], "definitions": [{"key": "pkg/__init__.py", "kind": "module", "name": "__init__", "start_line": 1, "end_line": 1, "exported": true}] }, { - "version": "13", "path": "pkg/loader.py", "language": "python", + "version": "14", "path": "pkg/loader.py", "language": "python", "imports": [{"line": 1, "module": "yaml", "alias": "yaml", "kind": "static", "namespace": "yaml"}], "definitions": [ {"key": "pkg/loader.py", "kind": "module", "name": "loader", "start_line": 1, "end_line": 4, "exported": true}, diff --git a/projectgraph/testdata/python/reexport-cycle/graph.golden.json b/projectgraph/testdata/python/reexport-cycle/graph.golden.json index e52ae5b7..d413c948 100644 --- a/projectgraph/testdata/python/reexport-cycle/graph.golden.json +++ b/projectgraph/testdata/python/reexport-cycle/graph.golden.json @@ -105,7 +105,7 @@ "main.py": "indexed" }, "stats": { - "version": "13", + "version": "14", "files": 3, "nodes": 3, "edges": 3, diff --git a/projectgraph/testdata/python/reexport-cycle/indexes.json b/projectgraph/testdata/python/reexport-cycle/indexes.json index 088fee96..d5dc08b8 100644 --- a/projectgraph/testdata/python/reexport-cycle/indexes.json +++ b/projectgraph/testdata/python/reexport-cycle/indexes.json @@ -1,17 +1,17 @@ [ { - "version": "13", "path": "main.py", "language": "python", + "version": "14", "path": "main.py", "language": "python", "imports": [{"line": 1, "module": "a", "item": "x", "alias": "x", "kind": "static", "namespace": "a//x"}], "definitions": [{"key": "main.py", "kind": "module", "name": "main", "start_line": 1, "end_line": 2, "exported": true}], "calls": [{"caller": "main.py", "line": 2, "column": 1, "callee": "x", "targets": ["a//x"], "kind": "call"}] }, { - "version": "13", "path": "a/__init__.py", "language": "python", + "version": "14", "path": "a/__init__.py", "language": "python", "imports": [{"line": 1, "module": "b", "item": "x", "alias": "x", "kind": "static", "namespace": "b//x"}], "definitions": [{"key": "a/__init__.py", "kind": "module", "name": "__init__", "start_line": 1, "end_line": 1, "exported": true}] }, { - "version": "13", "path": "b/__init__.py", "language": "python", + "version": "14", "path": "b/__init__.py", "language": "python", "imports": [{"line": 1, "module": "a", "item": "x", "alias": "x", "kind": "static", "namespace": "a//x"}], "definitions": [{"key": "b/__init__.py", "kind": "module", "name": "__init__", "start_line": 1, "end_line": 1, "exported": true}] } diff --git a/projectgraph/testdata/python/relative-imports/graph.golden.json b/projectgraph/testdata/python/relative-imports/graph.golden.json index 8edf0567..22bf82a6 100644 --- a/projectgraph/testdata/python/relative-imports/graph.golden.json +++ b/projectgraph/testdata/python/relative-imports/graph.golden.json @@ -154,7 +154,7 @@ "pkg/sub/b.py": "indexed" }, "stats": { - "version": "13", + "version": "14", "files": 3, "nodes": 6, "edges": 4, diff --git a/projectgraph/testdata/python/relative-imports/indexes.json b/projectgraph/testdata/python/relative-imports/indexes.json index 4248be64..db008b91 100644 --- a/projectgraph/testdata/python/relative-imports/indexes.json +++ b/projectgraph/testdata/python/relative-imports/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "13", "path": "pkg/sub/a.py", "language": "python", + "version": "14", "path": "pkg/sub/a.py", "language": "python", "imports": [ {"line": 1, "module": ".b", "item": "f", "alias": "f", "kind": "static", "namespace": "//b//f"}, {"line": 2, "module": "..c", "item": "g", "alias": "g", "kind": "static", "namespace": "////c//g"} @@ -15,14 +15,14 @@ ] }, { - "version": "13", "path": "pkg/sub/b.py", "language": "python", + "version": "14", "path": "pkg/sub/b.py", "language": "python", "definitions": [ {"key": "pkg/sub/b.py", "kind": "module", "name": "b", "start_line": 1, "end_line": 2, "exported": true}, {"key": "pkg/sub/b.py//f", "kind": "function", "name": "f", "start_line": 1, "end_line": 2, "exported": true} ] }, { - "version": "13", "path": "pkg/c.py", "language": "python", + "version": "14", "path": "pkg/c.py", "language": "python", "definitions": [ {"key": "pkg/c.py", "kind": "module", "name": "c", "start_line": 1, "end_line": 2, "exported": true}, {"key": "pkg/c.py//g", "kind": "function", "name": "g", "start_line": 1, "end_line": 2, "exported": true} diff --git a/projectgraph/testdata/python/sibling-import/graph.golden.json b/projectgraph/testdata/python/sibling-import/graph.golden.json index 9f033009..3de9f25e 100644 --- a/projectgraph/testdata/python/sibling-import/graph.golden.json +++ b/projectgraph/testdata/python/sibling-import/graph.golden.json @@ -98,7 +98,7 @@ "main.py": "indexed" }, "stats": { - "version": "13", + "version": "14", "files": 2, "nodes": 4, "edges": 3, diff --git a/projectgraph/testdata/python/sibling-import/indexes.json b/projectgraph/testdata/python/sibling-import/indexes.json index 790eba1a..b3c87e12 100644 --- a/projectgraph/testdata/python/sibling-import/indexes.json +++ b/projectgraph/testdata/python/sibling-import/indexes.json @@ -1,12 +1,12 @@ [ { - "version": "13", "path": "main.py", "language": "python", + "version": "14", "path": "main.py", "language": "python", "imports": [{"line": 1, "module": "handlers", "item": "parse", "alias": "parse", "kind": "static", "namespace": "handlers//parse"}], "definitions": [{"key": "main.py", "kind": "module", "name": "main", "start_line": 1, "end_line": 3, "exported": true}], "calls": [{"caller": "main.py", "line": 3, "column": 1, "callee": "parse", "targets": ["handlers//parse"], "kind": "call"}] }, { - "version": "13", "path": "handlers.py", "language": "python", + "version": "14", "path": "handlers.py", "language": "python", "imports": [{"line": 1, "module": "yaml", "alias": "yaml", "kind": "static", "namespace": "yaml"}], "definitions": [ {"key": "handlers.py", "kind": "module", "name": "handlers", "start_line": 1, "end_line": 4, "exported": true}, diff --git a/projectgraph/testdata/python/src-layout/graph.golden.json b/projectgraph/testdata/python/src-layout/graph.golden.json index 1c8dcf06..5205e3f7 100644 --- a/projectgraph/testdata/python/src-layout/graph.golden.json +++ b/projectgraph/testdata/python/src-layout/graph.golden.json @@ -99,7 +99,7 @@ "services/api/src/api/core.py": "indexed" }, "stats": { - "version": "13", + "version": "14", "files": 3, "nodes": 4, "edges": 2, diff --git a/projectgraph/testdata/python/src-layout/indexes.json b/projectgraph/testdata/python/src-layout/indexes.json index 74817fae..8dab6e9b 100644 --- a/projectgraph/testdata/python/src-layout/indexes.json +++ b/projectgraph/testdata/python/src-layout/indexes.json @@ -1,16 +1,16 @@ [ { - "version": "13", "path": "services/api/src/api/__init__.py", "language": "python", + "version": "14", "path": "services/api/src/api/__init__.py", "language": "python", "definitions": [{"key": "services/api/src/api/__init__.py", "kind": "module", "name": "__init__", "start_line": 1, "end_line": 1, "exported": true}] }, { - "version": "13", "path": "services/api/src/api/cli.py", "language": "python", + "version": "14", "path": "services/api/src/api/cli.py", "language": "python", "imports": [{"line": 1, "module": "api.core", "item": "run", "alias": "run", "kind": "static", "namespace": "api//core//run"}], "definitions": [{"key": "services/api/src/api/cli.py", "kind": "module", "name": "cli", "start_line": 1, "end_line": 2, "exported": true}], "calls": [{"caller": "services/api/src/api/cli.py", "line": 2, "column": 1, "callee": "run", "targets": ["api//core//run"], "kind": "call"}] }, { - "version": "13", "path": "services/api/src/api/core.py", "language": "python", + "version": "14", "path": "services/api/src/api/core.py", "language": "python", "definitions": [ {"key": "services/api/src/api/core.py", "kind": "module", "name": "core", "start_line": 1, "end_line": 2, "exported": true}, {"key": "services/api/src/api/core.py//run", "kind": "function", "name": "run", "start_line": 1, "end_line": 2, "exported": true} diff --git a/projectgraph/testdata/python/sys-path/graph.golden.json b/projectgraph/testdata/python/sys-path/graph.golden.json index 42aaf46a..13d43d63 100644 --- a/projectgraph/testdata/python/sys-path/graph.golden.json +++ b/projectgraph/testdata/python/sys-path/graph.golden.json @@ -82,7 +82,7 @@ "lib/shared.py": "indexed" }, "stats": { - "version": "13", + "version": "14", "files": 2, "nodes": 3, "edges": 2, diff --git a/projectgraph/testdata/python/sys-path/indexes.json b/projectgraph/testdata/python/sys-path/indexes.json index b4c7270b..372cc0fb 100644 --- a/projectgraph/testdata/python/sys-path/indexes.json +++ b/projectgraph/testdata/python/sys-path/indexes.json @@ -1,12 +1,12 @@ [ { - "version": "13", "path": "app/main.py", "language": "python", + "version": "14", "path": "app/main.py", "language": "python", "imports": [{"line": 1, "module": "shared", "alias": "shared", "kind": "static", "namespace": "shared"}], "definitions": [{"key": "app/main.py", "kind": "module", "name": "main", "start_line": 1, "end_line": 2, "exported": true}], "calls": [{"caller": "app/main.py", "line": 2, "column": 1, "callee": "shared.go", "targets": ["shared//go"], "receiver": "shared", "method": "go", "kind": "call"}] }, { - "version": "13", "path": "lib/shared.py", "language": "python", + "version": "14", "path": "lib/shared.py", "language": "python", "definitions": [ {"key": "lib/shared.py", "kind": "module", "name": "shared", "start_line": 1, "end_line": 2, "exported": true}, {"key": "lib/shared.py//go", "kind": "function", "name": "go", "start_line": 1, "end_line": 2, "exported": true} diff --git a/projectgraph/testdata/python/wildcard/graph.golden.json b/projectgraph/testdata/python/wildcard/graph.golden.json index a725c3c9..aef21c93 100644 --- a/projectgraph/testdata/python/wildcard/graph.golden.json +++ b/projectgraph/testdata/python/wildcard/graph.golden.json @@ -112,7 +112,7 @@ "util.py": "indexed" }, "stats": { - "version": "13", + "version": "14", "files": 2, "nodes": 4, "edges": 3, diff --git a/projectgraph/testdata/python/wildcard/indexes.json b/projectgraph/testdata/python/wildcard/indexes.json index 2d1be2df..8c56f11b 100644 --- a/projectgraph/testdata/python/wildcard/indexes.json +++ b/projectgraph/testdata/python/wildcard/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "13", "path": "main.py", "language": "python", + "version": "14", "path": "main.py", "language": "python", "imports": [{"line": 1, "module": "util", "wildcard": true, "kind": "static", "namespace": "util"}], "definitions": [{"key": "main.py", "kind": "module", "name": "main", "start_line": 1, "end_line": 3, "exported": true}], "calls": [ @@ -9,7 +9,7 @@ ] }, { - "version": "13", "path": "util.py", "language": "python", + "version": "14", "path": "util.py", "language": "python", "definitions": [ {"key": "util.py", "kind": "module", "name": "util", "start_line": 1, "end_line": 4, "exported": true}, {"key": "util.py//helper", "kind": "function", "name": "helper", "start_line": 1, "end_line": 2, "exported": true}, diff --git a/projectgraph/testdata/roots/extra-rules/graph.golden.json b/projectgraph/testdata/roots/extra-rules/graph.golden.json index 63a0afe1..c74d5060 100644 --- a/projectgraph/testdata/roots/extra-rules/graph.golden.json +++ b/projectgraph/testdata/roots/extra-rules/graph.golden.json @@ -105,7 +105,7 @@ "views.py": "indexed" }, "stats": { - "version": "13", + "version": "14", "files": 3, "nodes": 7, "edges": 0, diff --git a/projectgraph/testdata/roots/extra-rules/indexes.json b/projectgraph/testdata/roots/extra-rules/indexes.json index b6c142a8..12b245f9 100644 --- a/projectgraph/testdata/roots/extra-rules/indexes.json +++ b/projectgraph/testdata/roots/extra-rules/indexes.json @@ -1,10 +1,10 @@ [ { - "version": "13", "path": "app/users/route.ts", "language": "typescript", + "version": "14", "path": "app/users/route.ts", "language": "typescript", "definitions": [{"key": "app/users/route.ts", "kind": "module", "name": "route", "start_line": 1, "end_line": 1, "exported": true}] }, { - "version": "13", "path": "src/handler.js", "language": "javascript", + "version": "14", "path": "src/handler.js", "language": "javascript", "definitions": [ {"key": "src/handler.js", "kind": "module", "name": "handler", "start_line": 1, "end_line": 4, "exported": true}, {"key": "src/handler.js//Handler", "kind": "class", "name": "Handler", "start_line": 1, "end_line": 4, "exported": true}, @@ -13,7 +13,7 @@ ] }, { - "version": "13", "path": "views.py", "language": "python", + "version": "14", "path": "views.py", "language": "python", "definitions": [ {"key": "views.py", "kind": "module", "name": "views", "start_line": 1, "end_line": 3, "exported": true}, {"key": "views.py//index", "kind": "function", "name": "index", "start_line": 2, "end_line": 3, "exported": true, "decorators": ["@app.route"]} diff --git a/projectgraph/testdata/roots/library-surface/graph.golden.json b/projectgraph/testdata/roots/library-surface/graph.golden.json index 1f691c4e..83cab2c9 100644 --- a/projectgraph/testdata/roots/library-surface/graph.golden.json +++ b/projectgraph/testdata/roots/library-surface/graph.golden.json @@ -82,7 +82,7 @@ "lib/test_api.py": "indexed" }, "stats": { - "version": "13", + "version": "14", "files": 2, "nodes": 5, "edges": 0, diff --git a/projectgraph/testdata/roots/library-surface/indexes.json b/projectgraph/testdata/roots/library-surface/indexes.json index 16451349..a89db48a 100644 --- a/projectgraph/testdata/roots/library-surface/indexes.json +++ b/projectgraph/testdata/roots/library-surface/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "13", "path": "lib/api.py", "language": "python", + "version": "14", "path": "lib/api.py", "language": "python", "definitions": [ {"key": "lib/api.py", "kind": "module", "name": "api", "start_line": 1, "end_line": 4, "exported": true}, {"key": "lib/api.py//public", "kind": "function", "name": "public", "start_line": 1, "end_line": 2, "exported": true}, @@ -8,7 +8,7 @@ ] }, { - "version": "13", "path": "lib/test_api.py", "language": "python", + "version": "14", "path": "lib/test_api.py", "language": "python", "definitions": [ {"key": "lib/test_api.py", "kind": "module", "name": "test_api", "start_line": 1, "end_line": 2, "exported": true}, {"key": "lib/test_api.py//test_public", "kind": "function", "name": "test_public", "start_line": 1, "end_line": 2, "exported": true} diff --git a/projectgraph/testdata/roots/main-guard/graph.golden.json b/projectgraph/testdata/roots/main-guard/graph.golden.json index 93ac6b0b..48672199 100644 --- a/projectgraph/testdata/roots/main-guard/graph.golden.json +++ b/projectgraph/testdata/roots/main-guard/graph.golden.json @@ -60,7 +60,7 @@ "lib.py": "indexed" }, "stats": { - "version": "13", + "version": "14", "files": 2, "nodes": 4, "edges": 1, diff --git a/projectgraph/testdata/roots/main-guard/indexes.json b/projectgraph/testdata/roots/main-guard/indexes.json index c476e4a9..890a4e4b 100644 --- a/projectgraph/testdata/roots/main-guard/indexes.json +++ b/projectgraph/testdata/roots/main-guard/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "13", "path": "app.py", "language": "python", "main_guard": true, + "version": "14", "path": "app.py", "language": "python", "main_guard": true, "definitions": [ {"key": "app.py", "kind": "module", "name": "app", "start_line": 1, "end_line": 5, "exported": true}, {"key": "app.py//run", "kind": "function", "name": "run", "start_line": 1, "end_line": 2, "exported": true} @@ -8,7 +8,7 @@ "calls": [{"caller": "app.py", "line": 5, "column": 5, "callee": "run", "targets": ["app.py//run"], "kind": "call"}] }, { - "version": "13", "path": "lib.py", "language": "python", + "version": "14", "path": "lib.py", "language": "python", "definitions": [ {"key": "lib.py", "kind": "module", "name": "lib", "start_line": 1, "end_line": 2, "exported": true}, {"key": "lib.py//lib", "kind": "function", "name": "lib", "start_line": 1, "end_line": 2, "exported": true} diff --git a/projectgraph/testdata/roots/package-json-bin/graph.golden.json b/projectgraph/testdata/roots/package-json-bin/graph.golden.json index fb02953b..7abe7789 100644 --- a/projectgraph/testdata/roots/package-json-bin/graph.golden.json +++ b/projectgraph/testdata/roots/package-json-bin/graph.golden.json @@ -64,7 +64,7 @@ "lib/run.js": "indexed" }, "stats": { - "version": "13", + "version": "14", "files": 2, "nodes": 3, "edges": 2, diff --git a/projectgraph/testdata/roots/package-json-bin/indexes.json b/projectgraph/testdata/roots/package-json-bin/indexes.json index fdf03e94..ef2b0a99 100644 --- a/projectgraph/testdata/roots/package-json-bin/indexes.json +++ b/projectgraph/testdata/roots/package-json-bin/indexes.json @@ -1,12 +1,12 @@ [ { - "version": "13", "path": "bin/cli.js", "language": "javascript", + "version": "14", "path": "bin/cli.js", "language": "javascript", "imports": [{"line": 1, "module": "../lib/run", "item": "run", "alias": "run", "kind": "static", "namespace": "..//lib//run//run"}], "definitions": [{"key": "bin/cli.js", "kind": "module", "name": "cli", "start_line": 1, "end_line": 2, "exported": true}], "calls": [{"caller": "bin/cli.js", "line": 2, "column": 1, "callee": "run", "targets": ["..//lib//run//run"], "kind": "call"}] }, { - "version": "13", "path": "lib/run.js", "language": "javascript", + "version": "14", "path": "lib/run.js", "language": "javascript", "exports": [{"name": "run", "target": "lib/run.js//run", "line": 1}], "definitions": [ {"key": "lib/run.js", "kind": "module", "name": "run", "start_line": 1, "end_line": 1, "exported": true}, diff --git a/projectgraph/testdata/roots/package-json-main/graph.golden.json b/projectgraph/testdata/roots/package-json-main/graph.golden.json index 4775ede5..61013232 100644 --- a/projectgraph/testdata/roots/package-json-main/graph.golden.json +++ b/projectgraph/testdata/roots/package-json-main/graph.golden.json @@ -154,7 +154,7 @@ "parser.js": "indexed" }, "stats": { - "version": "13", + "version": "14", "files": 4, "nodes": 9, "edges": 3, diff --git a/projectgraph/testdata/roots/package-json-main/indexes.json b/projectgraph/testdata/roots/package-json-main/indexes.json index af5612eb..56e5e660 100644 --- a/projectgraph/testdata/roots/package-json-main/indexes.json +++ b/projectgraph/testdata/roots/package-json-main/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "13", + "version": "14", "path": "cli.js", "language": "javascript", "exports": [ @@ -30,7 +30,7 @@ ] }, { - "version": "13", + "version": "14", "path": "fmt.js", "language": "javascript", "exports": [ @@ -60,7 +60,7 @@ ] }, { - "version": "13", + "version": "14", "path": "index.js", "language": "javascript", "exports": [ @@ -104,7 +104,7 @@ ] }, { - "version": "13", + "version": "14", "path": "parser.js", "language": "javascript", "exports": [ diff --git a/projectgraph/testdata/roots/procfile-dockerfile/graph.golden.json b/projectgraph/testdata/roots/procfile-dockerfile/graph.golden.json index 2c82a473..ca59be78 100644 --- a/projectgraph/testdata/roots/procfile-dockerfile/graph.golden.json +++ b/projectgraph/testdata/roots/procfile-dockerfile/graph.golden.json @@ -108,7 +108,7 @@ "worker.py": "indexed" }, "stats": { - "version": "13", + "version": "14", "files": 6, "nodes": 7, "edges": 0, diff --git a/projectgraph/testdata/roots/procfile-dockerfile/indexes.json b/projectgraph/testdata/roots/procfile-dockerfile/indexes.json index 4cb4c178..3955f2b6 100644 --- a/projectgraph/testdata/roots/procfile-dockerfile/indexes.json +++ b/projectgraph/testdata/roots/procfile-dockerfile/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "13", + "version": "14", "path": "app/wsgi.py", "language": "python", "definitions": [ @@ -23,7 +23,7 @@ ] }, { - "version": "13", + "version": "14", "path": "app/cli.py", "language": "python", "definitions": [ @@ -38,7 +38,7 @@ ] }, { - "version": "13", + "version": "14", "path": "app/__init__.py", "language": "python", "definitions": [ @@ -53,7 +53,7 @@ ] }, { - "version": "13", + "version": "14", "path": "worker.py", "language": "python", "definitions": [ @@ -68,7 +68,7 @@ ] }, { - "version": "13", + "version": "14", "path": "ignored.py", "language": "python", "definitions": [ @@ -83,7 +83,7 @@ ] }, { - "version": "13", + "version": "14", "path": "web/server.js", "language": "javascript", "definitions": [ diff --git a/projectgraph/testdata/roots/pyproject-scripts/graph.golden.json b/projectgraph/testdata/roots/pyproject-scripts/graph.golden.json index 1b306710..1688beac 100644 --- a/projectgraph/testdata/roots/pyproject-scripts/graph.golden.json +++ b/projectgraph/testdata/roots/pyproject-scripts/graph.golden.json @@ -146,7 +146,7 @@ "src/app/tool.py": "indexed" }, "stats": { - "version": "13", + "version": "14", "files": 5, "nodes": 10, "edges": 0, diff --git a/projectgraph/testdata/roots/pyproject-scripts/indexes.json b/projectgraph/testdata/roots/pyproject-scripts/indexes.json index 12ea4146..38244e1b 100644 --- a/projectgraph/testdata/roots/pyproject-scripts/indexes.json +++ b/projectgraph/testdata/roots/pyproject-scripts/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "13", + "version": "14", "path": "src/app/cli.py", "language": "python", "definitions": [ @@ -23,7 +23,7 @@ ] }, { - "version": "13", + "version": "14", "path": "src/app/tool.py", "language": "python", "definitions": [ @@ -46,7 +46,7 @@ ] }, { - "version": "13", + "version": "14", "path": "src/app/plugin.py", "language": "python", "definitions": [ @@ -78,7 +78,7 @@ ] }, { - "version": "13", + "version": "14", "path": "src/app/legacy.py", "language": "python", "definitions": [ @@ -93,7 +93,7 @@ ] }, { - "version": "13", + "version": "14", "path": "src/app/ignored.py", "language": "python", "definitions": [ diff --git a/projectgraph/testdata/roots/setup-py/graph.golden.json b/projectgraph/testdata/roots/setup-py/graph.golden.json index 8d9fe88b..3e329aa3 100644 --- a/projectgraph/testdata/roots/setup-py/graph.golden.json +++ b/projectgraph/testdata/roots/setup-py/graph.golden.json @@ -74,7 +74,7 @@ "tool/main.py": "indexed" }, "stats": { - "version": "13", + "version": "14", "files": 2, "nodes": 4, "edges": 0, diff --git a/projectgraph/testdata/roots/setup-py/indexes.json b/projectgraph/testdata/roots/setup-py/indexes.json index 9423486b..5a20a349 100644 --- a/projectgraph/testdata/roots/setup-py/indexes.json +++ b/projectgraph/testdata/roots/setup-py/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "13", + "version": "14", "path": "tool/main.py", "language": "python", "definitions": [ @@ -23,7 +23,7 @@ ] }, { - "version": "13", + "version": "14", "path": "tool/admin.py", "language": "python", "definitions": [ diff --git a/projectgraph/testdata/roots/test-file/graph.golden.json b/projectgraph/testdata/roots/test-file/graph.golden.json index 01bcc76c..e0dcfc61 100644 --- a/projectgraph/testdata/roots/test-file/graph.golden.json +++ b/projectgraph/testdata/roots/test-file/graph.golden.json @@ -68,7 +68,7 @@ "tests/test_app.py": "indexed" }, "stats": { - "version": "13", + "version": "14", "files": 1, "nodes": 4, "edges": 0, diff --git a/projectgraph/testdata/roots/test-file/indexes.json b/projectgraph/testdata/roots/test-file/indexes.json index 9164dd6b..4059f41f 100644 --- a/projectgraph/testdata/roots/test-file/indexes.json +++ b/projectgraph/testdata/roots/test-file/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "13", "path": "tests/test_app.py", "language": "python", + "version": "14", "path": "tests/test_app.py", "language": "python", "definitions": [ {"key": "tests/test_app.py", "kind": "module", "name": "test_app", "start_line": 1, "end_line": 6, "exported": true}, {"key": "tests/test_app.py//TestApp", "kind": "class", "name": "TestApp", "start_line": 1, "end_line": 4, "exported": true}, diff --git a/projectgraph/testdata/typescript/js-specifier/graph.golden.json b/projectgraph/testdata/typescript/js-specifier/graph.golden.json index 39236b74..a8d326c0 100644 --- a/projectgraph/testdata/typescript/js-specifier/graph.golden.json +++ b/projectgraph/testdata/typescript/js-specifier/graph.golden.json @@ -82,7 +82,7 @@ "src/util.ts": "indexed" }, "stats": { - "version": "13", + "version": "14", "files": 2, "nodes": 3, "edges": 2, diff --git a/projectgraph/testdata/typescript/js-specifier/indexes.json b/projectgraph/testdata/typescript/js-specifier/indexes.json index 8139e260..680e8985 100644 --- a/projectgraph/testdata/typescript/js-specifier/indexes.json +++ b/projectgraph/testdata/typescript/js-specifier/indexes.json @@ -1,12 +1,12 @@ [ { - "version": "13", "path": "src/main.ts", "language": "typescript", + "version": "14", "path": "src/main.ts", "language": "typescript", "imports": [{"line": 1, "module": "./util.js", "item": "f", "alias": "f", "kind": "static", "namespace": ".//util.js//f"}], "definitions": [{"key": "src/main.ts", "kind": "module", "name": "main", "start_line": 1, "end_line": 2, "exported": true}], "calls": [{"caller": "src/main.ts", "line": 2, "column": 1, "callee": "f", "targets": [".//util.js//f"], "kind": "call"}] }, { - "version": "13", "path": "src/util.ts", "language": "typescript", + "version": "14", "path": "src/util.ts", "language": "typescript", "exports": [{"name": "f", "target": "src/util.ts//f", "line": 1}], "definitions": [ {"key": "src/util.ts", "kind": "module", "name": "util", "start_line": 1, "end_line": 1, "exported": true}, diff --git a/projectgraph/testdata/typescript/tsconfig-paths/graph.golden.json b/projectgraph/testdata/typescript/tsconfig-paths/graph.golden.json index 155930a9..9f28d7e6 100644 --- a/projectgraph/testdata/typescript/tsconfig-paths/graph.golden.json +++ b/projectgraph/testdata/typescript/tsconfig-paths/graph.golden.json @@ -138,7 +138,7 @@ "src/main.ts": "indexed" }, "stats": { - "version": "13", + "version": "14", "files": 3, "nodes": 5, "edges": 4, diff --git a/projectgraph/testdata/typescript/tsconfig-paths/indexes.json b/projectgraph/testdata/typescript/tsconfig-paths/indexes.json index f88701f1..8b8e5a16 100644 --- a/projectgraph/testdata/typescript/tsconfig-paths/indexes.json +++ b/projectgraph/testdata/typescript/tsconfig-paths/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "13", "path": "src/main.ts", "language": "typescript", + "version": "14", "path": "src/main.ts", "language": "typescript", "imports": [ {"line": 1, "module": "@app/server", "item": "serve", "alias": "serve", "kind": "static", "namespace": "@app//server//serve"}, {"line": 2, "module": "src/config", "item": "cfg", "alias": "cfg", "kind": "static", "namespace": "src//config//cfg"} @@ -12,7 +12,7 @@ ] }, { - "version": "13", "path": "src/app/server.ts", "language": "typescript", + "version": "14", "path": "src/app/server.ts", "language": "typescript", "exports": [{"name": "serve", "target": "src/app/server.ts//serve", "line": 1}], "definitions": [ {"key": "src/app/server.ts", "kind": "module", "name": "server", "start_line": 1, "end_line": 1, "exported": true}, @@ -20,7 +20,7 @@ ] }, { - "version": "13", "path": "src/config.ts", "language": "typescript", + "version": "14", "path": "src/config.ts", "language": "typescript", "exports": [{"name": "cfg", "target": "src/config.ts//cfg", "line": 1}], "definitions": [ {"key": "src/config.ts", "kind": "module", "name": "config", "start_line": 1, "end_line": 1, "exported": true}, diff --git a/projectgraph/testdata/typescript/type-only/graph.golden.json b/projectgraph/testdata/typescript/type-only/graph.golden.json index 9b634a6a..19f90df6 100644 --- a/projectgraph/testdata/typescript/type-only/graph.golden.json +++ b/projectgraph/testdata/typescript/type-only/graph.golden.json @@ -97,7 +97,7 @@ "src/types.ts": "indexed" }, "stats": { - "version": "13", + "version": "14", "files": 3, "nodes": 5, "edges": 2, diff --git a/projectgraph/testdata/typescript/type-only/indexes.json b/projectgraph/testdata/typescript/type-only/indexes.json index 2adfa519..3dc37beb 100644 --- a/projectgraph/testdata/typescript/type-only/indexes.json +++ b/projectgraph/testdata/typescript/type-only/indexes.json @@ -1,6 +1,6 @@ [ { - "version": "13", "path": "src/main.ts", "language": "typescript", + "version": "14", "path": "src/main.ts", "language": "typescript", "imports": [ {"line": 1, "module": "./types", "item": "Opts", "alias": "Opts", "kind": "type-only", "namespace": ".//types//Opts"}, {"line": 2, "module": "./decl", "item": "decl", "alias": "decl", "kind": "static", "namespace": ".//decl//decl"} @@ -9,7 +9,7 @@ "calls": [{"caller": "src/main.ts", "line": 3, "column": 1, "callee": "decl", "targets": [".//decl//decl"], "kind": "call"}] }, { - "version": "13", "path": "src/types.ts", "language": "typescript", + "version": "14", "path": "src/types.ts", "language": "typescript", "exports": [{"name": "Opts", "target": "src/types.ts//Opts", "line": 1}], "definitions": [ {"key": "src/types.ts", "kind": "module", "name": "types", "start_line": 1, "end_line": 1, "exported": true}, @@ -17,7 +17,7 @@ ] }, { - "version": "13", "path": "src/decl.d.ts", "language": "typescript", + "version": "14", "path": "src/decl.d.ts", "language": "typescript", "exports": [{"name": "decl", "target": "src/decl.d.ts//decl", "line": 1}], "definitions": [ {"key": "src/decl.d.ts", "kind": "module", "name": "decl", "start_line": 1, "end_line": 1, "exported": true}, diff --git a/projectgraph/walk.go b/projectgraph/walk.go index 7e504d4b..d0d32b73 100644 --- a/projectgraph/walk.go +++ b/projectgraph/walk.go @@ -69,8 +69,24 @@ func (b *builder) walkFrom(r resolution, names []string) resolution { // of a function or a method whose result has the next name, // when a unit of dependency code gives the function. if i+1 < len(names) { - if class, ok := b.externalResult(r.key, name); ok { - r.key, r.virtual, r.declared = class, true, true + // An unwrap that ends the names is the call of the method + // of the wrapper, so the walk takes it as a member. + if res, ok := b.externalResult(r.key, name); ok { + skip, typed := unwrap(res.wraps, names[i+1:]) + switch { + case !typed: + r.fail = &failure{reason: fileindex.UnresolvedUnknownReceiver, name: names[i+1]} + return r + case i+1+skip < len(names): + r.key, r.virtual, r.declared = res.class, true, true + i += 1 + skip + continue + } + } + // A type or a module of a unit gives the next name, as + // from_vec of smallvec::SmallVec::from_vec(v). + if defs, _ := b.unitDefinitions(derivedOrigins(b.origins[r.key], name)); len(defs) > 0 { + r.key = b.deriveKey(r.key, names[i:i+1]) i++ continue } @@ -79,7 +95,7 @@ func (b *builder) walkFrom(r resolution, names []string) resolution { r.rule = cmp.Or(r.rule, ruleExternal) return r case kind == NodeFunction || kind == NodeMethod || kind == NodeConstructor: - consumed, done = b.walkFunction(&r, name) + consumed, done = b.walkFunction(&r, names[i:]) default: r.fail = noDefinition(name) return r @@ -225,17 +241,24 @@ func (b *builder) walkClass(r *resolution, name string, last bool, instance *boo return false, true } -// walkFunction reads a name of a function: a Go call of the function gives -// a value of its first result, as New().Run(). Else the name is a local -// variable or a parameter of the function, and the index does not know its -// value. -func (b *builder) walkFunction(r *resolution, name string) (consumed, done bool) { - class, reason, ok := b.resultType(fileindex.PackageOf(r.key), r.key, 0) - if ok { - r.key, r.virtual, r.declared = class, true, true - return false, false +// walkFunction reads the names after a function: a call of the function +// gives a value of its first result, as New().Run() in Go. A Rust result +// in an Option or a Result takes unwrap or expect first. Else the name is a +// local variable or a parameter of the function, and the index does not +// know its value. +func (b *builder) walkFunction(r *resolution, names []string) (consumed, done bool) { + res, reason, ok := b.resultType(fileindex.PackageOf(r.key), r.key, 0) + skip, typed := unwrap(res.wraps, names) + switch { + case ok && typed && skip == 1 && len(names) == 1: + // An unwrap that ends the names runs the code of the function + // and the unwrap of std, so the walk ends at the function. + return true, false + case ok && typed: + r.key, r.virtual, r.declared = res.class, true, true + return skip == 1, false } - r.fail = &failure{reason: reason, name: name} + r.fail = &failure{reason: cmp.Or(reason, fileindex.UnresolvedUnknownReceiver), name: names[0]} return false, true } diff --git a/test/projectgraph/cases/go/dependency-build-files/summaries/go_github.com_acme_kit_v1.0.0.json b/test/projectgraph/cases/go/dependency-build-files/summaries/go_github.com_acme_kit_v1.0.0.json index 129306a5..f94ff60a 100644 --- a/test/projectgraph/cases/go/dependency-build-files/summaries/go_github.com_acme_kit_v1.0.0.json +++ b/test/projectgraph/cases/go/dependency-build-files/summaries/go_github.com_acme_kit_v1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", - "index_version": "13", - "link_version": "13", + "index_version": "14", + "link_version": "14", "unit": "go:github.com/acme/kit@v1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/go/dependency-generated-code/summaries/go_github.com_acme_kit_v1.0.0.json b/test/projectgraph/cases/go/dependency-generated-code/summaries/go_github.com_acme_kit_v1.0.0.json index fb6a75d2..4576755e 100644 --- a/test/projectgraph/cases/go/dependency-generated-code/summaries/go_github.com_acme_kit_v1.0.0.json +++ b/test/projectgraph/cases/go/dependency-generated-code/summaries/go_github.com_acme_kit_v1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", - "index_version": "13", - "link_version": "13", + "index_version": "14", + "link_version": "14", "unit": "go:github.com/acme/kit@v1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/go/dependency-implements-interface/summaries/go_github.com_acme_dep_v1.0.0.json b/test/projectgraph/cases/go/dependency-implements-interface/summaries/go_github.com_acme_dep_v1.0.0.json index 530e12a3..c1ed7457 100644 --- a/test/projectgraph/cases/go/dependency-implements-interface/summaries/go_github.com_acme_dep_v1.0.0.json +++ b/test/projectgraph/cases/go/dependency-implements-interface/summaries/go_github.com_acme_dep_v1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", - "index_version": "13", - "link_version": "13", + "index_version": "14", + "link_version": "14", "unit": "go:github.com/acme/dep@v1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/go/dependency-module/summaries/go_github.com_acme_yamlish_v1.2.0.json b/test/projectgraph/cases/go/dependency-module/summaries/go_github.com_acme_yamlish_v1.2.0.json index 7a343e6f..8ffe98a8 100644 --- a/test/projectgraph/cases/go/dependency-module/summaries/go_github.com_acme_yamlish_v1.2.0.json +++ b/test/projectgraph/cases/go/dependency-module/summaries/go_github.com_acme_yamlish_v1.2.0.json @@ -1,7 +1,7 @@ { "version": "2", - "index_version": "13", - "link_version": "13", + "index_version": "14", + "link_version": "14", "unit": "go:github.com/acme/yamlish@v1.2.0", "symbols": [ { diff --git a/test/projectgraph/cases/go/dependency-platform-files/summaries/go_github.com_acme_kit_v1.0.0.json b/test/projectgraph/cases/go/dependency-platform-files/summaries/go_github.com_acme_kit_v1.0.0.json index fb6a75d2..4576755e 100644 --- a/test/projectgraph/cases/go/dependency-platform-files/summaries/go_github.com_acme_kit_v1.0.0.json +++ b/test/projectgraph/cases/go/dependency-platform-files/summaries/go_github.com_acme_kit_v1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", - "index_version": "13", - "link_version": "13", + "index_version": "14", + "link_version": "14", "unit": "go:github.com/acme/kit@v1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/go/dependency-result-types/summaries/go_example.com_markup_v1.0.0.json b/test/projectgraph/cases/go/dependency-result-types/summaries/go_example.com_markup_v1.0.0.json index ae764cc0..a073ae94 100644 --- a/test/projectgraph/cases/go/dependency-result-types/summaries/go_example.com_markup_v1.0.0.json +++ b/test/projectgraph/cases/go/dependency-result-types/summaries/go_example.com_markup_v1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", - "index_version": "13", - "link_version": "13", + "index_version": "14", + "link_version": "14", "unit": "go:example.com/markup@v1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/go/dependency-result-types/summaries/go_std_go1.25.1.json b/test/projectgraph/cases/go/dependency-result-types/summaries/go_std_go1.25.1.json index 780d5871..be4042bc 100644 --- a/test/projectgraph/cases/go/dependency-result-types/summaries/go_std_go1.25.1.json +++ b/test/projectgraph/cases/go/dependency-result-types/summaries/go_std_go1.25.1.json @@ -1,7 +1,7 @@ { "version": "2", - "index_version": "13", - "link_version": "13", + "index_version": "14", + "link_version": "14", "unit": "go:std@go1.25.1", "symbols": [ { diff --git a/test/projectgraph/cases/javascript/dependency-candidates/summaries/npm_store-kit_1.0.0.json b/test/projectgraph/cases/javascript/dependency-candidates/summaries/npm_store-kit_1.0.0.json index 69390d5d..7b5c6f59 100644 --- a/test/projectgraph/cases/javascript/dependency-candidates/summaries/npm_store-kit_1.0.0.json +++ b/test/projectgraph/cases/javascript/dependency-candidates/summaries/npm_store-kit_1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", - "index_version": "13", - "link_version": "13", + "index_version": "14", + "link_version": "14", "unit": "npm:store-kit@1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/javascript/dependency-exports-import/summaries/npm_kit_1.0.0.json b/test/projectgraph/cases/javascript/dependency-exports-import/summaries/npm_kit_1.0.0.json index 32dad2c1..df5024b7 100644 --- a/test/projectgraph/cases/javascript/dependency-exports-import/summaries/npm_kit_1.0.0.json +++ b/test/projectgraph/cases/javascript/dependency-exports-import/summaries/npm_kit_1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", - "index_version": "13", - "link_version": "13", + "index_version": "14", + "link_version": "14", "unit": "npm:kit@1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/javascript/dependency-exports-require/summaries/npm_kit_1.0.0.json b/test/projectgraph/cases/javascript/dependency-exports-require/summaries/npm_kit_1.0.0.json index 32dad2c1..df5024b7 100644 --- a/test/projectgraph/cases/javascript/dependency-exports-require/summaries/npm_kit_1.0.0.json +++ b/test/projectgraph/cases/javascript/dependency-exports-require/summaries/npm_kit_1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", - "index_version": "13", - "link_version": "13", + "index_version": "14", + "link_version": "14", "unit": "npm:kit@1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/javascript/dependency-method-name/summaries/npm_plug_1.0.0.json b/test/projectgraph/cases/javascript/dependency-method-name/summaries/npm_plug_1.0.0.json index 7fb4d425..a68af1f0 100644 --- a/test/projectgraph/cases/javascript/dependency-method-name/summaries/npm_plug_1.0.0.json +++ b/test/projectgraph/cases/javascript/dependency-method-name/summaries/npm_plug_1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", - "index_version": "13", - "link_version": "13", + "index_version": "14", + "link_version": "14", "unit": "npm:plug@1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/javascript/dependency-same-path/summaries/npm_util-kit_1.0.0.json b/test/projectgraph/cases/javascript/dependency-same-path/summaries/npm_util-kit_1.0.0.json index 9c324a5e..0eb4e9be 100644 --- a/test/projectgraph/cases/javascript/dependency-same-path/summaries/npm_util-kit_1.0.0.json +++ b/test/projectgraph/cases/javascript/dependency-same-path/summaries/npm_util-kit_1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", - "index_version": "13", - "link_version": "13", + "index_version": "14", + "link_version": "14", "unit": "npm:util-kit@1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/javascript/dependency-transitive/summaries/npm_lodash._reinterpolate_3.0.0.json b/test/projectgraph/cases/javascript/dependency-transitive/summaries/npm_lodash._reinterpolate_3.0.0.json index 2ec04cfe..7bf0531b 100644 --- a/test/projectgraph/cases/javascript/dependency-transitive/summaries/npm_lodash._reinterpolate_3.0.0.json +++ b/test/projectgraph/cases/javascript/dependency-transitive/summaries/npm_lodash._reinterpolate_3.0.0.json @@ -1,7 +1,7 @@ { "version": "2", - "index_version": "13", - "link_version": "13", + "index_version": "14", + "link_version": "14", "unit": "npm:lodash._reinterpolate@3.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/javascript/dependency-transitive/summaries/npm_lodash_4.17.21.json b/test/projectgraph/cases/javascript/dependency-transitive/summaries/npm_lodash_4.17.21.json index e835a375..89c87485 100644 --- a/test/projectgraph/cases/javascript/dependency-transitive/summaries/npm_lodash_4.17.21.json +++ b/test/projectgraph/cases/javascript/dependency-transitive/summaries/npm_lodash_4.17.21.json @@ -1,7 +1,7 @@ { "version": "2", - "index_version": "13", - "link_version": "13", + "index_version": "14", + "link_version": "14", "unit": "npm:lodash@4.17.21", "symbols": [ { diff --git a/test/projectgraph/cases/python/dependency-dark-unit/summaries/pypi_PyYAML_6.0.1.json b/test/projectgraph/cases/python/dependency-dark-unit/summaries/pypi_PyYAML_6.0.1.json index 111172ef..e4aa580e 100644 --- a/test/projectgraph/cases/python/dependency-dark-unit/summaries/pypi_PyYAML_6.0.1.json +++ b/test/projectgraph/cases/python/dependency-dark-unit/summaries/pypi_PyYAML_6.0.1.json @@ -1,7 +1,7 @@ { "version": "2", - "index_version": "13", - "link_version": "13", + "index_version": "14", + "link_version": "14", "unit": "pypi:PyYAML@6.0.1", "symbols": [ { diff --git a/test/projectgraph/cases/python/dependency-failed-reached/summaries/pypi_badpkg_1.0.0.json b/test/projectgraph/cases/python/dependency-failed-reached/summaries/pypi_badpkg_1.0.0.json index 35366ed5..8b519218 100644 --- a/test/projectgraph/cases/python/dependency-failed-reached/summaries/pypi_badpkg_1.0.0.json +++ b/test/projectgraph/cases/python/dependency-failed-reached/summaries/pypi_badpkg_1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", - "index_version": "13", - "link_version": "13", + "index_version": "14", + "link_version": "14", "unit": "pypi:badpkg@1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/python/dependency-failed-reached/summaries/pypi_okpkg_1.0.0.json b/test/projectgraph/cases/python/dependency-failed-reached/summaries/pypi_okpkg_1.0.0.json index 579a38ea..464b2d14 100644 --- a/test/projectgraph/cases/python/dependency-failed-reached/summaries/pypi_okpkg_1.0.0.json +++ b/test/projectgraph/cases/python/dependency-failed-reached/summaries/pypi_okpkg_1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", - "index_version": "13", - "link_version": "13", + "index_version": "14", + "link_version": "14", "unit": "pypi:okpkg@1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/python/dependency-failed-unreached/summaries/pypi_badpkg_1.0.0.json b/test/projectgraph/cases/python/dependency-failed-unreached/summaries/pypi_badpkg_1.0.0.json index 35366ed5..8b519218 100644 --- a/test/projectgraph/cases/python/dependency-failed-unreached/summaries/pypi_badpkg_1.0.0.json +++ b/test/projectgraph/cases/python/dependency-failed-unreached/summaries/pypi_badpkg_1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", - "index_version": "13", - "link_version": "13", + "index_version": "14", + "link_version": "14", "unit": "pypi:badpkg@1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/python/dependency-failed-unreached/summaries/pypi_okpkg_1.0.0.json b/test/projectgraph/cases/python/dependency-failed-unreached/summaries/pypi_okpkg_1.0.0.json index 579a38ea..464b2d14 100644 --- a/test/projectgraph/cases/python/dependency-failed-unreached/summaries/pypi_okpkg_1.0.0.json +++ b/test/projectgraph/cases/python/dependency-failed-unreached/summaries/pypi_okpkg_1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", - "index_version": "13", - "link_version": "13", + "index_version": "14", + "link_version": "14", "unit": "pypi:okpkg@1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/python/dependency-package-dir/summaries/pypi_outer_1.0.0.json b/test/projectgraph/cases/python/dependency-package-dir/summaries/pypi_outer_1.0.0.json index e9345066..98cbddc4 100644 --- a/test/projectgraph/cases/python/dependency-package-dir/summaries/pypi_outer_1.0.0.json +++ b/test/projectgraph/cases/python/dependency-package-dir/summaries/pypi_outer_1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", - "index_version": "13", - "link_version": "13", + "index_version": "14", + "link_version": "14", "unit": "pypi:outer@1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/python/dependency-site-packages/summaries/pypi_requests_2.31.0.json b/test/projectgraph/cases/python/dependency-site-packages/summaries/pypi_requests_2.31.0.json index d5997158..bb94bfd5 100644 --- a/test/projectgraph/cases/python/dependency-site-packages/summaries/pypi_requests_2.31.0.json +++ b/test/projectgraph/cases/python/dependency-site-packages/summaries/pypi_requests_2.31.0.json @@ -1,7 +1,7 @@ { "version": "2", - "index_version": "13", - "link_version": "13", + "index_version": "14", + "link_version": "14", "unit": "pypi:requests@2.31.0", "symbols": [ { diff --git a/test/projectgraph/cases/python/dependency-site-packages/summaries/pypi_urllib3_2.0.7.json b/test/projectgraph/cases/python/dependency-site-packages/summaries/pypi_urllib3_2.0.7.json index 1a984add..bcc32e77 100644 --- a/test/projectgraph/cases/python/dependency-site-packages/summaries/pypi_urllib3_2.0.7.json +++ b/test/projectgraph/cases/python/dependency-site-packages/summaries/pypi_urllib3_2.0.7.json @@ -1,7 +1,7 @@ { "version": "2", - "index_version": "13", - "link_version": "13", + "index_version": "14", + "link_version": "14", "unit": "pypi:urllib3@2.0.7", "symbols": [ { diff --git a/test/projectgraph/cases/rust/dependency-crate/expect.yaml b/test/projectgraph/cases/rust/dependency-crate/expect.yaml new file mode 100644 index 00000000..5d74e145 --- /dev/null +++ b/test/projectgraph/cases/rust/dependency-crate/expect.yaml @@ -0,0 +1,170 @@ +source: safedep:hand +description: >- + main opens a kit::Store with Store::open().unwrap() and calls save on it, and flush::all calls flush on a value of + a generic type S: Sink. The code of the crate kit is the unit cargo:kit@1.0.0. open returns Result, + so unwrap gives a Store, and the call of save gets the external key kit//Store//save, which a package edge joins + to the method in the unit. The bound Sink is a trait of the unit, and Store implements it, so flush gets a cha + edge to kit//Store//flush, and the site keeps its record. Store is in a private module, and lib.rs gives it on + with pub use, so its key is kit//Store and not kit//store//Store. +units: + vendor/kit: cargo:kit@1.0.0 +roots: + - key: src/main.rs + kind: main + rule: rust.main + - key: src/main.rs//main + kind: main + rule: rust.main +exact_edges: true +edges: + - from: cargo:kit@1.0.0//src/store.rs//Store//flush + to: cargo:kit@1.0.0//src/store.rs//Store + kind: direct + confidence: high + site: cargo:kit@1.0.0//src/store.rs:18 + - from: cargo:kit@1.0.0//src/store.rs//Store//flush + to: std//fs//remove_file + kind: direct + confidence: high + site: cargo:kit@1.0.0//src/store.rs:19 + - from: cargo:kit@1.0.0//src/store.rs//Store//flush + to: std//fs//remove_file//unwrap + kind: direct + confidence: high + site: cargo:kit@1.0.0//src/store.rs:19 + - from: cargo:kit@1.0.0//src/store.rs//Store//open + to: Ok + kind: direct + confidence: high + site: cargo:kit@1.0.0//src/store.rs:9 + - from: cargo:kit@1.0.0//src/store.rs//Store//open + to: cargo:kit@1.0.0//src/store.rs//Store + kind: direct + confidence: high + site: cargo:kit@1.0.0//src/store.rs:9 + - from: cargo:kit@1.0.0//src/store.rs//Store//save + to: cargo:kit@1.0.0//src/store.rs//Store + kind: direct + confidence: high + site: cargo:kit@1.0.0//src/store.rs:12 + - from: cargo:kit@1.0.0//src/store.rs//Store//save + to: std//fs//write + kind: direct + confidence: high + site: cargo:kit@1.0.0//src/store.rs:13 + - from: cargo:kit@1.0.0//src/store.rs//Store//save + to: std//fs//write//unwrap + kind: direct + confidence: high + site: cargo:kit@1.0.0//src/store.rs:13 + - from: kit//Store//flush + to: cargo:kit@1.0.0//src/store.rs//Store//flush + kind: package + confidence: high + site: cargo:kit@1.0.0//src/store.rs:18 + - from: kit//Store//open + to: cargo:kit@1.0.0//src/store.rs//Store//open + kind: package + confidence: high + site: cargo:kit@1.0.0//src/store.rs:8 + - from: kit//Store//open//unwrap + to: cargo:kit@1.0.0//src/store.rs//Store//open + kind: package + confidence: high + site: cargo:kit@1.0.0//src/store.rs:8 + - from: kit//Store//save + to: cargo:kit@1.0.0//src/store.rs//Store//save + kind: package + confidence: high + site: cargo:kit@1.0.0//src/store.rs:12 + - from: src/flush.rs//all + to: kit//Store//flush + kind: cha + confidence: medium + site: src/flush.rs:4 + - from: src/main.rs//main + to: kit//Store//open + kind: direct + confidence: high + site: src/main.rs:6 + - from: src/main.rs//main + to: kit//Store//open//unwrap + kind: direct + confidence: high + site: src/main.rs:6 + - from: src/main.rs//main + to: kit//Store//save + kind: direct + confidence: high + site: src/main.rs:7 + - from: src/main.rs//main + to: src/flush.rs//all + kind: direct + confidence: high + site: src/main.rs:8 +unresolved: + - caller: cargo:kit@1.0.0//src/store.rs//Store//save + site: cargo:kit@1.0.0//src/store.rs:13 + reason: unknown-receiver + method: unwrap + - caller: cargo:kit@1.0.0//src/store.rs//Store//flush + site: cargo:kit@1.0.0//src/store.rs:19 + reason: unknown-receiver + method: unwrap + - caller: src/flush.rs//all + site: src/flush.rs:4 + reason: no-definition + method: flush + - caller: src/main.rs//main + site: src/main.rs:6 + reason: unknown-receiver + method: unwrap +results: + - target: kit//Store//save + status: found + path: + - key: src/main.rs//main + - key: kit//Store//save + site: src/main.rs:7 + kind: direct + confidence: high + call_sites: + - caller: src/main.rs//main + site: src/main.rs:7 + reached: true + - target: std//fs//write + status: found + path: + - key: src/main.rs//main + - key: kit//Store//save + site: src/main.rs:7 + kind: direct + confidence: high + - key: cargo:kit@1.0.0//src/store.rs//Store//save + site: cargo:kit@1.0.0//src/store.rs:12 + kind: package + confidence: high + - key: std//fs//write + site: cargo:kit@1.0.0//src/store.rs:13 + kind: direct + confidence: high + call_sites: + - caller: cargo:kit@1.0.0//src/store.rs//Store//save + site: cargo:kit@1.0.0//src/store.rs:13 + reached: true + - target: kit//Store//flush + status: found + path: + - key: src/main.rs//main + - key: src/flush.rs//all + site: src/main.rs:8 + kind: direct + confidence: high + - key: kit//Store//flush + site: src/flush.rs:4 + kind: cha + confidence: medium + call_sites: + - caller: src/flush.rs//all + site: src/flush.rs:4 + reached: true diff --git a/test/projectgraph/cases/rust/dependency-crate/project/Cargo.toml b/test/projectgraph/cases/rust/dependency-crate/project/Cargo.toml new file mode 100644 index 00000000..492d638f --- /dev/null +++ b/test/projectgraph/cases/rust/dependency-crate/project/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "app" +version = "0.1.0" +edition = "2021" + +[dependencies] +kit = "1.0.0" diff --git a/test/projectgraph/cases/rust/dependency-crate/project/src/flush.rs b/test/projectgraph/cases/rust/dependency-crate/project/src/flush.rs new file mode 100644 index 00000000..24d29799 --- /dev/null +++ b/test/projectgraph/cases/rust/dependency-crate/project/src/flush.rs @@ -0,0 +1,5 @@ +use kit::Sink; + +pub fn all(sink: &S) { + sink.flush(); +} diff --git a/test/projectgraph/cases/rust/dependency-crate/project/src/main.rs b/test/projectgraph/cases/rust/dependency-crate/project/src/main.rs new file mode 100644 index 00000000..1e6fa13f --- /dev/null +++ b/test/projectgraph/cases/rust/dependency-crate/project/src/main.rs @@ -0,0 +1,9 @@ +mod flush; + +use kit::Store; + +fn main() { + let store = Store::open().unwrap(); + store.save(); + flush::all(&store); +} diff --git a/test/projectgraph/cases/rust/dependency-crate/project/vendor/kit/Cargo.toml b/test/projectgraph/cases/rust/dependency-crate/project/vendor/kit/Cargo.toml new file mode 100644 index 00000000..58da5f79 --- /dev/null +++ b/test/projectgraph/cases/rust/dependency-crate/project/vendor/kit/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "kit" +version = "1.0.0" +edition = "2021" diff --git a/test/projectgraph/cases/rust/dependency-crate/project/vendor/kit/src/lib.rs b/test/projectgraph/cases/rust/dependency-crate/project/vendor/kit/src/lib.rs new file mode 100644 index 00000000..6f15fdc1 --- /dev/null +++ b/test/projectgraph/cases/rust/dependency-crate/project/vendor/kit/src/lib.rs @@ -0,0 +1,7 @@ +mod store; + +pub use store::Store; + +pub trait Sink { + fn flush(&self); +} diff --git a/test/projectgraph/cases/rust/dependency-crate/project/vendor/kit/src/store.rs b/test/projectgraph/cases/rust/dependency-crate/project/vendor/kit/src/store.rs new file mode 100644 index 00000000..83c7fc95 --- /dev/null +++ b/test/projectgraph/cases/rust/dependency-crate/project/vendor/kit/src/store.rs @@ -0,0 +1,21 @@ +use crate::Sink; + +pub struct Store { + path: String, +} + +impl Store { + pub fn open() -> Result { + Ok(Store { path: String::new() }) + } + + pub fn save(&self) { + std::fs::write(&self.path, "data").unwrap(); + } +} + +impl Sink for Store { + fn flush(&self) { + std::fs::remove_file(&self.path).unwrap(); + } +} diff --git a/test/projectgraph/cases/rust/dependency-crate/summaries/cargo_kit_1.0.0.json b/test/projectgraph/cases/rust/dependency-crate/summaries/cargo_kit_1.0.0.json new file mode 100644 index 00000000..19ab23ab --- /dev/null +++ b/test/projectgraph/cases/rust/dependency-crate/summaries/cargo_kit_1.0.0.json @@ -0,0 +1,74 @@ +{ + "version": "2", + "index_version": "14", + "link_version": "14", + "unit": "cargo:kit@1.0.0", + "symbols": [ + { + "key": "kit//Sink", + "definitions": [ + "cargo:kit@1.0.0//src/lib.rs//Sink" + ], + "reaches": [] + }, + { + "key": "kit//Store", + "definitions": [ + "cargo:kit@1.0.0//src/store.rs//Store" + ], + "reaches": [] + }, + { + "key": "kit//Store//flush", + "definitions": [ + "cargo:kit@1.0.0//src/store.rs//Store//flush" + ], + "reaches": [], + "uncertainties": [ + 0 + ] + }, + { + "key": "kit//Store//open", + "definitions": [ + "cargo:kit@1.0.0//src/store.rs//Store//open" + ], + "reaches": [] + }, + { + "key": "kit//Store//save", + "definitions": [ + "cargo:kit@1.0.0//src/store.rs//Store//save" + ], + "reaches": [], + "uncertainties": [ + 1 + ] + } + ], + "targets": [], + "uncertainties": [ + { + "kind": "unresolved-call", + "key": "cargo:kit@1.0.0//src/store.rs//Store//flush", + "site": { + "path": "cargo:kit@1.0.0//src/store.rs", + "line": 19, + "column": 9 + }, + "text": "std::fs::remove_file(\u0026self.path).unwrap (unknown-receiver)", + "method": "unwrap" + }, + { + "kind": "unresolved-call", + "key": "cargo:kit@1.0.0//src/store.rs//Store//save", + "site": { + "path": "cargo:kit@1.0.0//src/store.rs", + "line": 13, + "column": 9 + }, + "text": "std::fs::write(\u0026self.path, \"data\").unwrap (unknown-receiver)", + "method": "unwrap" + } + ] +} diff --git a/test/projectgraph/receivers_test.go b/test/projectgraph/receivers_test.go index cfecdca5..051fa023 100644 --- a/test/projectgraph/receivers_test.go +++ b/test/projectgraph/receivers_test.go @@ -128,72 +128,54 @@ var rustReceiverRows = []receiverRow{ source: "fn run(mut t: Thing) {\n t.insert(1);\n}", target: "dep//Thing//insert", want: "found", - gap: "FOU-544", - today: "uncertain/unknown-receiver", }, { name: "reference and generic parameter", source: "fn run(t: &mut Thing<[u8; 4]>) {\n t.insert([1; 4]);\n}", target: "dep//Thing//insert", want: "found", - gap: "FOU-544", - today: "uncertain/unknown-receiver", }, { name: "struct field", source: "struct App {\n t: Thing,\n}\n\nfn run(a: &mut App) {\n a.t.insert(1);\n}", target: "dep//Thing//insert", want: "found", - gap: "FOU-544", - today: "uncertain/no-definition", }, { name: "first-party function return", source: "fn make() -> Thing {\n Thing::new()\n}\n\nfn run() {\n let mut t = make();\n t.insert(1);\n}", target: "dep//Thing//insert", want: "found", - gap: "FOU-544", - today: "uncertain/unknown-receiver", }, { name: "external function return", source: "fn run() {\n let mut t = dep::new_thing();\n t.insert(1);\n}", target: "dep//Thing//insert", want: "found", - gap: "FOU-544", - today: "uncertain/unknown-receiver", }, { name: "multi-return with ?", source: "fn run() -> Result<(), String> {\n let mut t = dep::load_thing()?;\n t.insert(1);\n Ok(())\n}", target: "dep//Thing//insert", want: "found", - gap: "FOU-544", - today: "uncertain/unknown-receiver", }, { name: "option unwrap", source: "fn run() {\n let mut t = dep::try_thing().unwrap();\n t.insert(1);\n}", target: "dep//Thing//insert", want: "found", - gap: "FOU-544", - today: "uncertain/unknown-receiver", }, { name: "chained call", source: "fn run() {\n dep::new_thing().insert(1);\n}", target: "dep//Thing//insert", want: "found", - gap: "FOU-544", - today: "uncertain/unknown-receiver", }, { name: "associated function", source: "fn run() {\n let mut t = Thing::from_vec(vec![1u8]);\n t.insert(1);\n}", target: "dep//Thing//insert", want: "found", - gap: "FOU-544", - today: "uncertain/unknown-receiver", }, { name: "associated function with type arguments", @@ -206,32 +188,24 @@ var rustReceiverRows = []receiverRow{ source: "fn run() {\n let a: Thing = Thing::new();\n let mut b = a.clone();\n b.insert(1);\n}", target: "dep//Thing//insert", want: "found", - gap: "FOU-544", - today: "uncertain/unknown-receiver", }, { name: "trait object return", source: "fn run() {\n let d = dep::doer();\n d.run();\n}", target: "dep//Thing//run", want: "found", - gap: "FOU-544", - today: "uncertain/unknown-receiver", }, { name: "generic bound", source: "fn run(d: &T) {\n d.run();\n}", target: "dep//Thing//run", want: "found", - gap: "FOU-544", - today: "uncertain/unknown-receiver", }, { name: "trait object with no implementation", source: "fn run(r: &dyn Runner) {\n r.start();\n}", target: "dep//Job//start", want: "uncertain/trait-receiver", - gap: "FOU-544", - today: "uncertain/unknown-receiver", }, } diff --git a/test/projectgraph/report.md b/test/projectgraph/report.md index 8a5740fa..24966c46 100644 --- a/test/projectgraph/report.md +++ b/test/projectgraph/report.md @@ -905,8 +905,9 @@ has no main, so every exported symbol is a root. The "before" rows come from the commit of FOU-542, before any fix of this measure. The "FOU-543" rows come from the commit of FOU-543, which also -holds the fixes of FOU-545, FOU-546 and FOU-547 before it. The Rust rows of -FOU-543 do not change, so the table leaves them out. +holds the fixes of FOU-545, FOU-546 and FOU-547 before it. The "FOU-544" +rows come from the commit of FOU-544. The Rust rows of FOU-543 and the Go +function row of FOU-544 do not change, so the table leaves them out. | Language | Kind | Run | Targets | found | not-found | no-such-target | unknown-receiver | no-definition | interface-receiver | no-dependency-source | budget | ambiguous | computed-name | | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | @@ -914,15 +915,21 @@ FOU-543 do not change, so the table leaves them out. | Go | function | FOU-543 | 256 | 13 | 0 | 7 | 3 | 0 | 0 | 3 | 39 | 132 | 59 | | Go | method | before | 1298 | 29 | 2 | 15 | 294 | 119 | 0 | 0 | 576 | 158 | 105 | | Go | method | FOU-543 | 1298 | 30 | 2 | 15 | 319 | 112 | 3 | 2 | 123 | 459 | 233 | +| Go | method | FOU-544 | 1298 | 30 | 2 | 15 | 320 | 112 | 3 | 2 | 123 | 459 | 232 | | Rust | function | before | 13 | 0 | 0 | 10 | 3 | 0 | 0 | 0 | 0 | 0 | 0 | +| Rust | function | FOU-544 | 13 | 1 | 0 | 5 | 0 | 5 | 0 | 0 | 0 | 2 | 0 | | Rust | method | before | 38 | 0 | 1 | 19 | 13 | 5 | 0 | 0 | 0 | 0 | 0 | +| Rust | method | FOU-544 | 38 | 0 | 0 | 7 | 11 | 11 | 0 | 0 | 0 | 9 | 0 | | Rust | other | before | 5 | 0 | 0 | 5 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +| Rust | other | FOU-544 | 5 | 0 | 0 | 5 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | | Language | Run | Method targets that end as unknown-receiver | | --- | --- | --- | | Go | before | 0.227 (294/1298) | | Go | FOU-543 | 0.246 (319/1298) | +| Go | FOU-544 | 0.247 (320/1298) | | Rust | before | 0.342 (13/38) | +| Rust | FOU-544 | 0.289 (11/38) | Before: @@ -942,9 +949,9 @@ Before: After FOU-543: - Go: no project has a truncation of the link step. The graphs of the 10 - Go projects hold 3,435 files, 44% of the 7,810 of before, because the - index step leaves out the test files and the files that the build of - linux/amd64 does not read. `budget` + Go projects hold 5,286 files, 68% of the 7,810 of before, because the + index step leaves out the test files and the files that no Go build + reads by default. It keeps the files of every platform. `budget` falls from 576 to 123 method targets. The rest is scany: two `tables.go` files of x/text are over the size limit of the index step, so they fail. @@ -958,10 +965,44 @@ After FOU-543: advisory are methods that the code of the dependency calls inside its own module, as the methods of the HTTP/2 server of x/net. A call inside a unit goes to the definition of the method and not to its external key, - so the search reaches the definition and the target, which is the - external key, reads as uncertain. The receiver forms of FOU-543 make the + so the search reaches the definition and never meets the target, which + is the external key. The target reads as uncertain when an unresolved + call concerns it, and as not-found when a call of the project that no + root reaches names it. FOU-555 fixes the match. The receiver forms of FOU-543 make the calls of the project find the method, as the receiver table and the cases show. They do not change how a target matches code inside a unit. - The measure has no target of the standard library. The Go vulndb has the most method symbols for it, and FOU-543 adds `GOROOT/src` as a unit, but the corpus pins no Go release. + +After FOU-544: + +- Rust: the link step links the code of each crate. The 10 Rust graphs + hold 1,389 files, against 193 of before, and no project has a problem + of a crate that the link step does not link. +- Rust: `no-such-target` falls from 19 to 7 method targets and from 10 to + 5 function targets, because the graph now holds the keys of the crates. + lettre_email finds one function target. The other targets end + uncertain, with an unknown receiver, a call of a name with no + definition, or an `ambiguous` call that the search reaches. As for Go, + a target that only the code of its own crate calls reads as uncertain, + because a call inside a unit goes to the definition and not to the + external key. +- Rust: the five `other` targets of parking_lot name types of lock_api, + as `lock_api::MappedMutexGuard`. A type has no call, so they stay + `no-such-target`. +- Go: the rows move by one target between two causes, because a result + type of an external walk is now always its external key. + +After the review fixes of FOU-544 and FOU-547, the measure gives the same +counts as the FOU-544 rows. The index now keeps the files of every +platform and the bodies of generated files, which changes the graphs and +not the first uncertainty of any result. + +The `unknown-receiver` share counts a target when any reached site has an +unresolved receiver with the method name of the target, also a site +inside a unit. More reached code of the units gives more name matches on +`Next`, `Read` or `ServeHTTP`. So the share rises as the search reaches +more code, and it is not a measure of the receiver forms. The receiver +table of FOU-542 measures those, and every row of it is found. + diff --git a/test/projectgraph/version_test.go b/test/projectgraph/version_test.go index 946ad323..a1e20c4b 100644 --- a/test/projectgraph/version_test.go +++ b/test/projectgraph/version_test.go @@ -28,6 +28,7 @@ var graphHashes = map[string]string{ "11/12": "14d5bbbaca07ab950f75c665f48e71ce9195da2ebd853cd1b0b745d42c4852cc", "12/12": "1cb170ec83fe3517e6f71755983c040210f83bb7a0e69528b3d7007dcc69d5dc", "13/13": "9383a0bc5c6e8b760beb443112cae1400c39212c1da5c2d155c22488eb00eaa6", + "14/14": "0f3c9c38b39f4de1835f34e8a58bcea85461f37e7aa2d5a2dd484569e670ff48", } // resultHashes records the hash of the reach results of every case for @@ -40,6 +41,7 @@ var resultHashes = map[string]string{ "11/12/2": "8341f2a1965f9d3b19d976ff1ba63ed26c1e15026e02079ec6195dfc0d762dba", "12/12/2": "b89a142078d885eeb73fe15c731315c60d42b38270efd12a7857be44b9ccc83c", "13/13/2": "2f81b00e1708cbb7e0589f34eaa7866a5658a6cfc0a7608d532381ca4267ba06", + "14/14/2": "a832ea709bf3df852814375a56e6329c35e2ff4c49ae34ee49afe042e9502d17", } // summaryHashes records the hash of the golden summaries of the cases for @@ -53,6 +55,7 @@ var summaryHashes = map[string]string{ "11/12/2": "f4102bd40a876fa387f7ff1ce81505e1f04b64cd8165602d0726f897e2b88a7f", "12/12/2": "cb2147fda018110f38254519bbc75a814b99e0318f5c7485fbce78a667bb4287", "13/13/2": "dc844b1cd13e7873f54d646f27384c78b64707e13ae9c9ef27a4557ba7ba4dfc", + "14/14/2": "27424c4e2525450479faba78421959738748811d70b9a93b81e2bbdf1733bf0d", } func TestGraphHashFollowsVersion(t *testing.T) { From 7e0a52c4dd9a5dfb6eeeb80eda1367e90d359b22 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 19 Oct 2026 09:37:42 +0000 Subject: [PATCH 7/9] fix(reach): match a target against the external key of unit definitions (FOU-555) A call inside a unit of dependency code goes to the definition, as go:example.com/dep@v1.0.0//dep.go//Vuln, and not to the external key example.com//dep//Vuln that a target names. So the search never met the target. A target that only the code of its unit calls read as no-such-target, or as not-found when an unreached call of the project named it. This is how most vulnerable symbols are called, as the methods of the HTTP/2 server of x/net. With dependency units, vet would say "not called" for code that runs. The link step now sets Node.External on each function, method and constructor of a unit: the key that an importer writes for it. reach matches a target against the external node of its key and against each definition with that external key, also for a wildcard target. The call sites of a target leave out the package edges, which join a key to its definition. The match showed a second gap. A Rust library crate with a build.rs had the build script as its only root, so the library-surface roots were off, and a call of its public API read as not-found. cookie calls rand::thread_rng from Key::generate this way. A build script, an example or a benchmark of a Rust crate is now a helper program: its root stays, and it does not turn off the library-surface roots. The cases go/dependency-internal-call, rust/dependency-internal-call and rust/library-build-script check both. The method measure now finds 331 of 1,298 Go method targets (30 before), 51 of 256 Go function targets (13) and 16 of 38 Rust method targets (0), and no target is not-found. projectgraph.Version is 15 and reach.Version is 3. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01PFQbqPqd9xn1fT6hBsyoUm Signed-off-by: Claude --- docs/projectgraph.md | 10 ++- projectgraph/graph.go | 8 ++- projectgraph/link.go | 1 + projectgraph/profile.go | 5 ++ projectgraph/reach/reach.go | 47 ++++++++++--- projectgraph/results.go | 45 +++++++++--- projectgraph/roots.go | 3 +- projectgraph/rust.go | 17 +++++ projectgraph/rust_test.go | 20 ++++++ .../javascript/barrel-chain/graph.golden.json | 2 +- .../commonjs-require/graph.golden.json | 2 +- .../default-import/graph.golden.json | 2 +- .../esm-named-import/graph.golden.json | 2 +- .../javascript/export-star/graph.golden.json | 2 +- .../exports-conditions/graph.golden.json | 2 +- .../unknown-receiver/graph.golden.json | 2 +- .../javascript/workspaces/graph.golden.json | 2 +- .../python/all-hides/graph.golden.json | 2 +- .../python/assignment/graph.golden.json | 2 +- .../python/class-body/graph.golden.json | 2 +- .../python/decorators/graph.golden.json | 2 +- .../python/external/graph.golden.json | 2 +- .../python/local-receiver/graph.golden.json | 2 +- .../python/member-call/graph.golden.json | 2 +- .../python/missing-file/graph.golden.json | 2 +- .../python/package-init/graph.golden.json | 2 +- .../python/reexport-cycle/graph.golden.json | 2 +- .../python/relative-imports/graph.golden.json | 2 +- .../python/sibling-import/graph.golden.json | 2 +- .../python/src-layout/graph.golden.json | 2 +- .../python/sys-path/graph.golden.json | 2 +- .../python/wildcard/graph.golden.json | 2 +- .../roots/extra-rules/graph.golden.json | 2 +- .../roots/library-surface/graph.golden.json | 2 +- .../roots/main-guard/graph.golden.json | 2 +- .../roots/package-json-bin/graph.golden.json | 2 +- .../roots/package-json-main/graph.golden.json | 2 +- .../procfile-dockerfile/graph.golden.json | 2 +- .../roots/pyproject-scripts/graph.golden.json | 2 +- .../testdata/roots/setup-py/graph.golden.json | 2 +- .../roots/test-file/graph.golden.json | 2 +- .../typescript/js-specifier/graph.golden.json | 2 +- .../tsconfig-paths/graph.golden.json | 2 +- .../typescript/type-only/graph.golden.json | 2 +- .../go_github.com_acme_kit_v1.0.0.json | 2 +- .../go_github.com_acme_kit_v1.0.0.json | 2 +- .../go_github.com_acme_dep_v1.0.0.json | 2 +- .../go/dependency-internal-call/expect.yaml | 69 +++++++++++++++++++ .../dependency-internal-call/project/go.mod | 5 ++ .../gomod/github.com/acme/kit@v1.0.0/go.mod | 3 + .../gomod/github.com/acme/kit@v1.0.0/kit.go | 16 +++++ .../dependency-internal-call/project/main.go | 7 ++ .../go_github.com_acme_kit_v1.0.0.json | 23 +++++++ .../go_github.com_acme_yamlish_v1.2.0.json | 2 +- .../go_github.com_acme_kit_v1.0.0.json | 2 +- .../go_example.com_markup_v1.0.0.json | 2 +- .../summaries/go_std_go1.25.1.json | 2 +- .../summaries/npm_store-kit_1.0.0.json | 2 +- .../summaries/npm_kit_1.0.0.json | 2 +- .../summaries/npm_kit_1.0.0.json | 2 +- .../summaries/npm_plug_1.0.0.json | 2 +- .../summaries/npm_util-kit_1.0.0.json | 2 +- .../npm_lodash._reinterpolate_3.0.0.json | 2 +- .../summaries/npm_lodash_4.17.21.json | 2 +- .../summaries/pypi_PyYAML_6.0.1.json | 2 +- .../summaries/pypi_badpkg_1.0.0.json | 2 +- .../summaries/pypi_okpkg_1.0.0.json | 2 +- .../summaries/pypi_badpkg_1.0.0.json | 2 +- .../summaries/pypi_okpkg_1.0.0.json | 2 +- .../summaries/pypi_outer_1.0.0.json | 2 +- .../summaries/pypi_requests_2.31.0.json | 2 +- .../summaries/pypi_urllib3_2.0.7.json | 2 +- .../summaries/cargo_kit_1.0.0.json | 2 +- .../rust/dependency-internal-call/expect.yaml | 36 ++++++++++ .../project/Cargo.toml | 7 ++ .../project/src/main.rs | 3 + .../project/vendor/kit/Cargo.toml | 4 ++ .../project/vendor/kit/src/lib.rs | 5 ++ .../summaries/cargo_kit_1.0.0.json | 16 +++++ .../rust/library-build-script/expect.yaml | 40 +++++++++++ .../library-build-script/project/Cargo.toml | 7 ++ .../library-build-script/project/build.rs | 3 + .../library-build-script/project/src/lib.rs | 3 + .../project/vendor/kit/Cargo.toml | 4 ++ .../project/vendor/kit/src/lib.rs | 1 + .../summaries/cargo_kit_1.0.0.json | 16 +++++ test/projectgraph/report.md | 27 ++++++++ test/projectgraph/version_test.go | 3 + 88 files changed, 490 insertions(+), 80 deletions(-) create mode 100644 test/projectgraph/cases/go/dependency-internal-call/expect.yaml create mode 100644 test/projectgraph/cases/go/dependency-internal-call/project/go.mod create mode 100644 test/projectgraph/cases/go/dependency-internal-call/project/gomod/github.com/acme/kit@v1.0.0/go.mod create mode 100644 test/projectgraph/cases/go/dependency-internal-call/project/gomod/github.com/acme/kit@v1.0.0/kit.go create mode 100644 test/projectgraph/cases/go/dependency-internal-call/project/main.go create mode 100644 test/projectgraph/cases/go/dependency-internal-call/summaries/go_github.com_acme_kit_v1.0.0.json create mode 100644 test/projectgraph/cases/rust/dependency-internal-call/expect.yaml create mode 100644 test/projectgraph/cases/rust/dependency-internal-call/project/Cargo.toml create mode 100644 test/projectgraph/cases/rust/dependency-internal-call/project/src/main.rs create mode 100644 test/projectgraph/cases/rust/dependency-internal-call/project/vendor/kit/Cargo.toml create mode 100644 test/projectgraph/cases/rust/dependency-internal-call/project/vendor/kit/src/lib.rs create mode 100644 test/projectgraph/cases/rust/dependency-internal-call/summaries/cargo_kit_1.0.0.json create mode 100644 test/projectgraph/cases/rust/library-build-script/expect.yaml create mode 100644 test/projectgraph/cases/rust/library-build-script/project/Cargo.toml create mode 100644 test/projectgraph/cases/rust/library-build-script/project/build.rs create mode 100644 test/projectgraph/cases/rust/library-build-script/project/src/lib.rs create mode 100644 test/projectgraph/cases/rust/library-build-script/project/vendor/kit/Cargo.toml create mode 100644 test/projectgraph/cases/rust/library-build-script/project/vendor/kit/src/lib.rs create mode 100644 test/projectgraph/cases/rust/library-build-script/summaries/cargo_kit_1.0.0.json diff --git a/docs/projectgraph.md b/docs/projectgraph.md index a60dd92d..ddfb13c6 100644 --- a/docs/projectgraph.md +++ b/docs/projectgraph.md @@ -335,6 +335,14 @@ the key, so a query never holds a key that no node can have. | `reach.TargetByKey("yaml//load")` | The key, as a configuration or a flag writes it | `yaml//load` | | `reach.TargetByImport(langcode.Go, "os/exec", "Command")` | The module as the source of the language writes it, and the names under it | `os//exec//Command` | +A target also matches each function, method and constructor of a unit of +dependency code whose `Node.External` is the key, as the unexported +`go:golang.org/x/net@v0.61.0//html/render.go//render1` for +`golang.org//x//net//html//render1`. A call inside a unit goes to the +definition and not to the external key, so a target that only the code of +its unit calls is found through the definition. The call sites of a +target leave out the package edges, which join a key to its definition. + `reach.TargetsByKey` reads a list of keys. `Target.Key` returns the key, and the JSON form of a target is its key. One breadth-first search from the roots, and from the sources of the query, serves every target of a @@ -349,7 +357,7 @@ what the search did. | `found` | A path from a root reaches the target. `Path` holds the shortest one | | `not-found` | No path reaches the target, and no uncertainty concerns it | | `uncertain` | No path reaches the target, and an uncertainty concerns it | -| `no-such-target` | The key names no external node of the graph, and no uncertainty could name it | +| `no-such-target` | The key names no external node and no definition of a unit of the graph, and no uncertainty could name it | `reach.Result.String` writes a result as lines, and `reach.PathString` writes a path as `main.py:3 -> handlers.py:12 -> yaml//load`. diff --git a/projectgraph/graph.go b/projectgraph/graph.go index 4164d6a4..7f41133b 100644 --- a/projectgraph/graph.go +++ b/projectgraph/graph.go @@ -17,7 +17,7 @@ import ( // Version is the version of the link rules. Bump it when a rule that makes a // node, an edge, a root or an unresolved site changes, so a diff of two // reports shows the change. -const Version = "14" +const Version = "15" type NodeKind string @@ -163,7 +163,10 @@ func (s Site) String() string { } // Node is a definition, a module top level, a re-exported name or an -// external target. An external node has only its key and kind. +// external target. An external node has only its key and kind. External +// is the key that an importer of the package writes for a function, a +// method or a constructor of a unit of dependency code, as +// golang.org//x//net//html//render1. type Node struct { Key string `json:"key"` Kind NodeKind `json:"kind"` @@ -172,6 +175,7 @@ type Node struct { Exported bool `json:"exported,omitempty"` Test bool `json:"test,omitempty"` Flags []fileindex.Flag `json:"flags,omitempty"` + External string `json:"external,omitempty"` } // Edge is one resolved step from a caller to a callee. Resolver names the diff --git a/projectgraph/link.go b/projectgraph/link.go index 96ac1a0a..4f511f41 100644 --- a/projectgraph/link.go +++ b/projectgraph/link.go @@ -447,6 +447,7 @@ func (b *builder) copyUnresolved(ix *fileindex.Index) { } func (b *builder) finish() *Graph { + b.markExternals() g := b.g g.Edges = b.edges.sorted() g.Unresolved = sortedKeys(b.unresolved, compareUnresolved) diff --git a/projectgraph/profile.go b/projectgraph/profile.go index d0f7b556..a1d52387 100644 --- a/projectgraph/profile.go +++ b/projectgraph/profile.go @@ -188,6 +188,11 @@ type profile struct { // mainLoadsPackage marks a language whose program initializes the // package of main before main runs, as Go. mainLoadsPackage bool + // helperProgram reports whether the main of a file is a helper + // program of the project and not the program that its users run, as a + // build script or an example of a Rust crate. Its root stays, and it + // does not turn off the library-surface roots. + helperProgram func(path string) bool } // profiles are the languages of the link step, in a fixed order. The units diff --git a/projectgraph/reach/reach.go b/projectgraph/reach/reach.go index 6bdb4cfc..873e77b4 100644 --- a/projectgraph/reach/reach.go +++ b/projectgraph/reach/reach.go @@ -21,7 +21,9 @@ import ( // uncertainty, the tie rule of the paths, the caps and the fields of a // hop. Bump it when a rule changes, so a diff of two results shows the // change. Version 2 added the root kind to the first hop of a path. -const Version = "2" +// Version 3 matches a target against the external key of a definition of a +// unit of dependency code. +const Version = "3" const ( defaultMaxDepth = 64 @@ -358,6 +360,10 @@ type adjacency struct { out [][]int32 in [][]int32 externals []int + // unitDefs holds the definitions of units of dependency code with an + // external key, by id, and byExternal the same by the key. + unitDefs []int + byExternal map[string][]int includeTests bool @@ -392,11 +398,17 @@ func newAdjacency(g *projectgraph.Graph) *adjacency { slices.Sort(keys) keys = slices.Compact(keys) - a := &adjacency{g: g, keys: keys, id: make(map[string]int, len(keys)), out: make([][]int32, len(keys)), in: make([][]int32, len(keys))} + a := &adjacency{g: g, keys: keys, id: make(map[string]int, len(keys)), out: make([][]int32, len(keys)), in: make([][]int32, len(keys)), byExternal: map[string][]int{}} for i, k := range keys { a.id[k] = i - if n := g.Nodes[k]; n != nil && n.Kind == projectgraph.NodeExternal { + n := g.Nodes[k] + switch { + case n == nil: + case n.Kind == projectgraph.NodeExternal: a.externals = append(a.externals, i) + case n.External != "": + a.unitDefs = append(a.unitDefs, i) + a.byExternal[n.External] = append(a.byExternal[n.External], i) } } for i, e := range g.Edges { @@ -508,21 +520,31 @@ func (a *adjacency) methodReaches(name string, target Target) bool { return slices.ContainsFunc(a.match(target), func(id int) bool { return c[id] }) } -// match returns the external nodes of a target, by id. +// match returns the nodes of a target, by id: the external node of its +// key, and each definition of a unit of dependency code whose external key +// it names. A call inside a unit goes to the definition, so the search +// reaches a target that only the code of its unit calls through it. func (a *adjacency) match(target Target) []int { + var ids []int if _, ok := target.wildcard(); ok { - var ids []int for _, id := range a.externals { if target.Matches(a.keys[id]) { ids = append(ids, id) } } - return ids - } - if n := a.g.Nodes[target.Key()]; n != nil && n.Kind == projectgraph.NodeExternal { - return []int{a.id[target.Key()]} + for _, id := range a.unitDefs { + if target.Matches(a.g.Nodes[a.keys[id]].External) { + ids = append(ids, id) + } + } + } else { + if n := a.g.Nodes[target.Key()]; n != nil && n.Kind == projectgraph.NodeExternal { + ids = append(ids, a.id[target.Key()]) + } + ids = append(ids, a.byExternal[target.Key()]...) } - return nil + slices.Sort(ids) + return ids } func (a *adjacency) ids(roots []projectgraph.Root) []int { @@ -642,6 +664,11 @@ func (a *adjacency) callSites(ids []int, s *search) []CallSite { for _, id := range ids { for _, i := range a.in[id] { e := &a.g.Edges[i] + // A package edge joins the external key of a target to its + // definition. The calls of the key are its own call sites. + if e.Kind == projectgraph.EdgePackage { + continue + } sites = append(sites, CallSite{Caller: e.From, Site: e.Site, Reached: s.dist[a.id[e.From]] >= 0}) } } diff --git a/projectgraph/results.go b/projectgraph/results.go index c82d6d0b..291734a0 100644 --- a/projectgraph/results.go +++ b/projectgraph/results.go @@ -185,26 +185,55 @@ func (b *builder) entryKey(from, def string) string { // dependency code, as an import of its package names it, and records the // import as an origin of the key for a file of the importer unit. func (b *builder) symbolKey(importer, def string) (string, bool) { + key, o, ok := b.unitSymbol(def) + if !ok { + return "", false + } + o.unit = importer + if !slices.ContainsFunc(b.origins[key], o.equal) { + b.origins[key] = append(b.origins[key], o) + } + return key, true +} + +// unitSymbol returns the external key of a definition of a unit of +// dependency code, and the origin of an import that names it, with no +// importer unit. +func (b *builder) unitSymbol(def string) (string, origin, bool) { unit := fileindex.PackageOf(def) sp := b.spaces[unit] if unit == "" || sp == nil { - return "", false + return "", origin{}, false } lang := b.languageOf(def) symbol := profileOf(lang.Family()).symbol if symbol == nil { - return "", false + return "", origin{}, false } module, names, ok := symbol(b, sp, def) if !ok { - return "", false + return "", origin{}, false } - key := fileindex.Join(fileindex.Namespace(lang, module), names...) - o := origin{lang: lang, unit: importer, module: module, form: modules.FormImport, names: names} - if !slices.ContainsFunc(b.origins[key], o.equal) { - b.origins[key] = append(b.origins[key], o) + return fileindex.Join(fileindex.Namespace(lang, module), names...), origin{lang: lang, module: module, form: modules.FormImport, names: names}, true +} + +// markExternals sets the external key of each function, method and +// constructor of a unit of dependency code. A call that the unit makes to +// its own code goes to the definition and not to the external key, so +// reach matches a target against the external key of the definition too. +// Without it, a target that only the code of its unit calls reads as +// not-found, as the methods of the HTTP/2 server of x/net. +func (b *builder) markExternals() { + for _, n := range b.g.Nodes { + switch n.Kind { + case NodeFunction, NodeMethod, NodeConstructor: + default: + continue + } + if key, _, ok := b.unitSymbol(n.Key); ok { + n.External = key + } } - return key, true } // importedWalk resolves the names under an import that no file of the diff --git a/projectgraph/roots.go b/projectgraph/roots.go index 93466abd..33483fad 100644 --- a/projectgraph/roots.go +++ b/projectgraph/roots.go @@ -50,7 +50,8 @@ func (b *builder) markRoots(ctx context.Context) error { entry := false for _, r := range found { b.addRootAt(r.Key, r.Kind, r.Rule, Site(r.Site)) - entry = entry || r.Kind != RootTest + helper := b.profileAt(r.Key).helperProgram + entry = entry || r.Kind != RootTest && (helper == nil || !helper(fileindex.Unit(r.Key))) // A Go program initializes its package before main runs, so the // module top level of each file of the package is a root too. if u := b.unitOf[fileindex.Unit(r.Key)]; r.Kind == RootMain && u != nil && b.profileAt(r.Key).mainLoadsPackage { diff --git a/projectgraph/rust.go b/projectgraph/rust.go index 995b8946..7ddb2e15 100644 --- a/projectgraph/rust.go +++ b/projectgraph/rust.go @@ -41,6 +41,23 @@ var rustProfile = profile{ implicitMethods: rustImplicit, forwardMethod: "deref", methodAliases: rustConversions, + helperProgram: rustHelperProgram, +} + +// rustHelperProgram reports whether a file is the build script, an example +// or a benchmark of a crate. A library crate with a build.rs has a main, +// but its users call its public items. Without this, the build script +// made the only root of the crate, and a call of the library read as +// not-found. +func rustHelperProgram(path string) bool { + dir, file := "", path + if i := strings.LastIndex(path, "/"); i >= 0 { + dir, file = path[:i], path[i+1:] + } + if file == "build.rs" { + return true + } + return slices.ContainsFunc(strings.Split(dir, "/"), func(d string) bool { return d == "examples" || d == "benches" }) } // rustConversions name the methods of the traits of std that a call of diff --git a/projectgraph/rust_test.go b/projectgraph/rust_test.go index 4c82e52b..d50164d8 100644 --- a/projectgraph/rust_test.go +++ b/projectgraph/rust_test.go @@ -83,3 +83,23 @@ func TestBuildRustWildcardUseOfTheClassInItsImpl(t *testing.T) { require.NoError(t, err) assert.Equal(t, NodeClass, g.Nodes[class].Kind) } + +func TestRustHelperProgram(t *testing.T) { + for _, tt := range []struct { + path string + want bool + }{ + {path: "build.rs", want: true}, + {path: "kit/build.rs", want: true}, + {path: "examples/demo.rs", want: true}, + {path: "kit/examples/demo/main.rs", want: true}, + {path: "benches/speed.rs", want: true}, + {path: "src/main.rs", want: false}, + {path: "src/bin/tool.rs", want: false}, + {path: "src/examples.rs", want: false}, + } { + t.Run(tt.path, func(t *testing.T) { + assert.Equal(t, tt.want, rustHelperProgram(tt.path)) + }) + } +} diff --git a/projectgraph/testdata/javascript/barrel-chain/graph.golden.json b/projectgraph/testdata/javascript/barrel-chain/graph.golden.json index 491e3dd3..43ca85a2 100644 --- a/projectgraph/testdata/javascript/barrel-chain/graph.golden.json +++ b/projectgraph/testdata/javascript/barrel-chain/graph.golden.json @@ -174,7 +174,7 @@ "src/main.js": "indexed" }, "stats": { - "version": "14", + "version": "15", "files": 4, "nodes": 7, "edges": 6, diff --git a/projectgraph/testdata/javascript/commonjs-require/graph.golden.json b/projectgraph/testdata/javascript/commonjs-require/graph.golden.json index 8d0ae176..13192bb9 100644 --- a/projectgraph/testdata/javascript/commonjs-require/graph.golden.json +++ b/projectgraph/testdata/javascript/commonjs-require/graph.golden.json @@ -138,7 +138,7 @@ "main.js": "indexed" }, "stats": { - "version": "14", + "version": "15", "files": 3, "nodes": 5, "edges": 4, diff --git a/projectgraph/testdata/javascript/default-import/graph.golden.json b/projectgraph/testdata/javascript/default-import/graph.golden.json index ed8eeee9..dd13406e 100644 --- a/projectgraph/testdata/javascript/default-import/graph.golden.json +++ b/projectgraph/testdata/javascript/default-import/graph.golden.json @@ -138,7 +138,7 @@ "make.js": "indexed" }, "stats": { - "version": "14", + "version": "15", "files": 3, "nodes": 5, "edges": 4, diff --git a/projectgraph/testdata/javascript/esm-named-import/graph.golden.json b/projectgraph/testdata/javascript/esm-named-import/graph.golden.json index d067bd0c..10ba461d 100644 --- a/projectgraph/testdata/javascript/esm-named-import/graph.golden.json +++ b/projectgraph/testdata/javascript/esm-named-import/graph.golden.json @@ -166,7 +166,7 @@ "src/util.js": "indexed" }, "stats": { - "version": "14", + "version": "15", "files": 3, "nodes": 6, "edges": 5, diff --git a/projectgraph/testdata/javascript/export-star/graph.golden.json b/projectgraph/testdata/javascript/export-star/graph.golden.json index fb622523..28231c41 100644 --- a/projectgraph/testdata/javascript/export-star/graph.golden.json +++ b/projectgraph/testdata/javascript/export-star/graph.golden.json @@ -276,7 +276,7 @@ "main.js": "indexed" }, "stats": { - "version": "14", + "version": "15", "files": 5, "nodes": 11, "edges": 10, diff --git a/projectgraph/testdata/javascript/exports-conditions/graph.golden.json b/projectgraph/testdata/javascript/exports-conditions/graph.golden.json index 9c2f46c9..2ff50fe9 100644 --- a/projectgraph/testdata/javascript/exports-conditions/graph.golden.json +++ b/projectgraph/testdata/javascript/exports-conditions/graph.golden.json @@ -144,7 +144,7 @@ "lib/src/feature/y.js": "indexed" }, "stats": { - "version": "14", + "version": "15", "files": 4, "nodes": 7, "edges": 4, diff --git a/projectgraph/testdata/javascript/unknown-receiver/graph.golden.json b/projectgraph/testdata/javascript/unknown-receiver/graph.golden.json index 211643eb..130c54fa 100644 --- a/projectgraph/testdata/javascript/unknown-receiver/graph.golden.json +++ b/projectgraph/testdata/javascript/unknown-receiver/graph.golden.json @@ -124,7 +124,7 @@ "main.js": "indexed" }, "stats": { - "version": "14", + "version": "15", "files": 1, "nodes": 5, "edges": 3, diff --git a/projectgraph/testdata/javascript/workspaces/graph.golden.json b/projectgraph/testdata/javascript/workspaces/graph.golden.json index cc6054a2..84461092 100644 --- a/projectgraph/testdata/javascript/workspaces/graph.golden.json +++ b/projectgraph/testdata/javascript/workspaces/graph.golden.json @@ -127,7 +127,7 @@ "packages/core/util.js": "indexed" }, "stats": { - "version": "14", + "version": "15", "files": 3, "nodes": 6, "edges": 5, diff --git a/projectgraph/testdata/python/all-hides/graph.golden.json b/projectgraph/testdata/python/all-hides/graph.golden.json index bb4f422e..916b313e 100644 --- a/projectgraph/testdata/python/all-hides/graph.golden.json +++ b/projectgraph/testdata/python/all-hides/graph.golden.json @@ -174,7 +174,7 @@ "pkg/a.py": "indexed" }, "stats": { - "version": "14", + "version": "15", "files": 3, "nodes": 7, "edges": 6, diff --git a/projectgraph/testdata/python/assignment/graph.golden.json b/projectgraph/testdata/python/assignment/graph.golden.json index dca6a39c..44b94484 100644 --- a/projectgraph/testdata/python/assignment/graph.golden.json +++ b/projectgraph/testdata/python/assignment/graph.golden.json @@ -70,7 +70,7 @@ "main.py": "indexed" }, "stats": { - "version": "14", + "version": "15", "files": 1, "nodes": 3, "edges": 2, diff --git a/projectgraph/testdata/python/class-body/graph.golden.json b/projectgraph/testdata/python/class-body/graph.golden.json index 3319884e..0aa6e4f7 100644 --- a/projectgraph/testdata/python/class-body/graph.golden.json +++ b/projectgraph/testdata/python/class-body/graph.golden.json @@ -224,7 +224,7 @@ "config.py": "indexed" }, "stats": { - "version": "14", + "version": "15", "files": 1, "nodes": 12, "edges": 7, diff --git a/projectgraph/testdata/python/decorators/graph.golden.json b/projectgraph/testdata/python/decorators/graph.golden.json index 2e4026e6..327a0165 100644 --- a/projectgraph/testdata/python/decorators/graph.golden.json +++ b/projectgraph/testdata/python/decorators/graph.golden.json @@ -194,7 +194,7 @@ "app.py": "indexed" }, "stats": { - "version": "14", + "version": "15", "files": 1, "nodes": 9, "edges": 5, diff --git a/projectgraph/testdata/python/external/graph.golden.json b/projectgraph/testdata/python/external/graph.golden.json index 324dbf61..070797df 100644 --- a/projectgraph/testdata/python/external/graph.golden.json +++ b/projectgraph/testdata/python/external/graph.golden.json @@ -108,7 +108,7 @@ "main.py": "indexed" }, "stats": { - "version": "14", + "version": "15", "files": 1, "nodes": 5, "edges": 3, diff --git a/projectgraph/testdata/python/local-receiver/graph.golden.json b/projectgraph/testdata/python/local-receiver/graph.golden.json index eca478a1..e8f105cb 100644 --- a/projectgraph/testdata/python/local-receiver/graph.golden.json +++ b/projectgraph/testdata/python/local-receiver/graph.golden.json @@ -145,7 +145,7 @@ "main.py": "indexed" }, "stats": { - "version": "14", + "version": "15", "files": 1, "nodes": 5, "edges": 3, diff --git a/projectgraph/testdata/python/member-call/graph.golden.json b/projectgraph/testdata/python/member-call/graph.golden.json index 1c3a4bf0..36c7475e 100644 --- a/projectgraph/testdata/python/member-call/graph.golden.json +++ b/projectgraph/testdata/python/member-call/graph.golden.json @@ -258,7 +258,7 @@ "parser.py": "indexed" }, "stats": { - "version": "14", + "version": "15", "files": 3, "nodes": 9, "edges": 10, diff --git a/projectgraph/testdata/python/missing-file/graph.golden.json b/projectgraph/testdata/python/missing-file/graph.golden.json index 3edc0d2a..f57c6daa 100644 --- a/projectgraph/testdata/python/missing-file/graph.golden.json +++ b/projectgraph/testdata/python/missing-file/graph.golden.json @@ -55,7 +55,7 @@ "main.py": "cached" }, "stats": { - "version": "14", + "version": "15", "files": 2, "nodes": 2, "edges": 2, diff --git a/projectgraph/testdata/python/package-init/graph.golden.json b/projectgraph/testdata/python/package-init/graph.golden.json index 989d63d1..0f605f44 100644 --- a/projectgraph/testdata/python/package-init/graph.golden.json +++ b/projectgraph/testdata/python/package-init/graph.golden.json @@ -144,7 +144,7 @@ "pkg/loader.py": "indexed" }, "stats": { - "version": "14", + "version": "15", "files": 3, "nodes": 6, "edges": 5, diff --git a/projectgraph/testdata/python/reexport-cycle/graph.golden.json b/projectgraph/testdata/python/reexport-cycle/graph.golden.json index d413c948..b4af31d2 100644 --- a/projectgraph/testdata/python/reexport-cycle/graph.golden.json +++ b/projectgraph/testdata/python/reexport-cycle/graph.golden.json @@ -105,7 +105,7 @@ "main.py": "indexed" }, "stats": { - "version": "14", + "version": "15", "files": 3, "nodes": 3, "edges": 3, diff --git a/projectgraph/testdata/python/relative-imports/graph.golden.json b/projectgraph/testdata/python/relative-imports/graph.golden.json index 22bf82a6..09b99a12 100644 --- a/projectgraph/testdata/python/relative-imports/graph.golden.json +++ b/projectgraph/testdata/python/relative-imports/graph.golden.json @@ -154,7 +154,7 @@ "pkg/sub/b.py": "indexed" }, "stats": { - "version": "14", + "version": "15", "files": 3, "nodes": 6, "edges": 4, diff --git a/projectgraph/testdata/python/sibling-import/graph.golden.json b/projectgraph/testdata/python/sibling-import/graph.golden.json index 3de9f25e..b2031df1 100644 --- a/projectgraph/testdata/python/sibling-import/graph.golden.json +++ b/projectgraph/testdata/python/sibling-import/graph.golden.json @@ -98,7 +98,7 @@ "main.py": "indexed" }, "stats": { - "version": "14", + "version": "15", "files": 2, "nodes": 4, "edges": 3, diff --git a/projectgraph/testdata/python/src-layout/graph.golden.json b/projectgraph/testdata/python/src-layout/graph.golden.json index 5205e3f7..659fba14 100644 --- a/projectgraph/testdata/python/src-layout/graph.golden.json +++ b/projectgraph/testdata/python/src-layout/graph.golden.json @@ -99,7 +99,7 @@ "services/api/src/api/core.py": "indexed" }, "stats": { - "version": "14", + "version": "15", "files": 3, "nodes": 4, "edges": 2, diff --git a/projectgraph/testdata/python/sys-path/graph.golden.json b/projectgraph/testdata/python/sys-path/graph.golden.json index 13d43d63..b7f23a3b 100644 --- a/projectgraph/testdata/python/sys-path/graph.golden.json +++ b/projectgraph/testdata/python/sys-path/graph.golden.json @@ -82,7 +82,7 @@ "lib/shared.py": "indexed" }, "stats": { - "version": "14", + "version": "15", "files": 2, "nodes": 3, "edges": 2, diff --git a/projectgraph/testdata/python/wildcard/graph.golden.json b/projectgraph/testdata/python/wildcard/graph.golden.json index aef21c93..03ea7941 100644 --- a/projectgraph/testdata/python/wildcard/graph.golden.json +++ b/projectgraph/testdata/python/wildcard/graph.golden.json @@ -112,7 +112,7 @@ "util.py": "indexed" }, "stats": { - "version": "14", + "version": "15", "files": 2, "nodes": 4, "edges": 3, diff --git a/projectgraph/testdata/roots/extra-rules/graph.golden.json b/projectgraph/testdata/roots/extra-rules/graph.golden.json index c74d5060..4ff46249 100644 --- a/projectgraph/testdata/roots/extra-rules/graph.golden.json +++ b/projectgraph/testdata/roots/extra-rules/graph.golden.json @@ -105,7 +105,7 @@ "views.py": "indexed" }, "stats": { - "version": "14", + "version": "15", "files": 3, "nodes": 7, "edges": 0, diff --git a/projectgraph/testdata/roots/library-surface/graph.golden.json b/projectgraph/testdata/roots/library-surface/graph.golden.json index 83cab2c9..f2fc364e 100644 --- a/projectgraph/testdata/roots/library-surface/graph.golden.json +++ b/projectgraph/testdata/roots/library-surface/graph.golden.json @@ -82,7 +82,7 @@ "lib/test_api.py": "indexed" }, "stats": { - "version": "14", + "version": "15", "files": 2, "nodes": 5, "edges": 0, diff --git a/projectgraph/testdata/roots/main-guard/graph.golden.json b/projectgraph/testdata/roots/main-guard/graph.golden.json index 48672199..1f84fa12 100644 --- a/projectgraph/testdata/roots/main-guard/graph.golden.json +++ b/projectgraph/testdata/roots/main-guard/graph.golden.json @@ -60,7 +60,7 @@ "lib.py": "indexed" }, "stats": { - "version": "14", + "version": "15", "files": 2, "nodes": 4, "edges": 1, diff --git a/projectgraph/testdata/roots/package-json-bin/graph.golden.json b/projectgraph/testdata/roots/package-json-bin/graph.golden.json index 7abe7789..0767050c 100644 --- a/projectgraph/testdata/roots/package-json-bin/graph.golden.json +++ b/projectgraph/testdata/roots/package-json-bin/graph.golden.json @@ -64,7 +64,7 @@ "lib/run.js": "indexed" }, "stats": { - "version": "14", + "version": "15", "files": 2, "nodes": 3, "edges": 2, diff --git a/projectgraph/testdata/roots/package-json-main/graph.golden.json b/projectgraph/testdata/roots/package-json-main/graph.golden.json index 61013232..ae5ae680 100644 --- a/projectgraph/testdata/roots/package-json-main/graph.golden.json +++ b/projectgraph/testdata/roots/package-json-main/graph.golden.json @@ -154,7 +154,7 @@ "parser.js": "indexed" }, "stats": { - "version": "14", + "version": "15", "files": 4, "nodes": 9, "edges": 3, diff --git a/projectgraph/testdata/roots/procfile-dockerfile/graph.golden.json b/projectgraph/testdata/roots/procfile-dockerfile/graph.golden.json index ca59be78..3b6d8839 100644 --- a/projectgraph/testdata/roots/procfile-dockerfile/graph.golden.json +++ b/projectgraph/testdata/roots/procfile-dockerfile/graph.golden.json @@ -108,7 +108,7 @@ "worker.py": "indexed" }, "stats": { - "version": "14", + "version": "15", "files": 6, "nodes": 7, "edges": 0, diff --git a/projectgraph/testdata/roots/pyproject-scripts/graph.golden.json b/projectgraph/testdata/roots/pyproject-scripts/graph.golden.json index 1688beac..5f12c9d0 100644 --- a/projectgraph/testdata/roots/pyproject-scripts/graph.golden.json +++ b/projectgraph/testdata/roots/pyproject-scripts/graph.golden.json @@ -146,7 +146,7 @@ "src/app/tool.py": "indexed" }, "stats": { - "version": "14", + "version": "15", "files": 5, "nodes": 10, "edges": 0, diff --git a/projectgraph/testdata/roots/setup-py/graph.golden.json b/projectgraph/testdata/roots/setup-py/graph.golden.json index 3e329aa3..e812cfe7 100644 --- a/projectgraph/testdata/roots/setup-py/graph.golden.json +++ b/projectgraph/testdata/roots/setup-py/graph.golden.json @@ -74,7 +74,7 @@ "tool/main.py": "indexed" }, "stats": { - "version": "14", + "version": "15", "files": 2, "nodes": 4, "edges": 0, diff --git a/projectgraph/testdata/roots/test-file/graph.golden.json b/projectgraph/testdata/roots/test-file/graph.golden.json index e0dcfc61..183bfd7c 100644 --- a/projectgraph/testdata/roots/test-file/graph.golden.json +++ b/projectgraph/testdata/roots/test-file/graph.golden.json @@ -68,7 +68,7 @@ "tests/test_app.py": "indexed" }, "stats": { - "version": "14", + "version": "15", "files": 1, "nodes": 4, "edges": 0, diff --git a/projectgraph/testdata/typescript/js-specifier/graph.golden.json b/projectgraph/testdata/typescript/js-specifier/graph.golden.json index a8d326c0..138c4d69 100644 --- a/projectgraph/testdata/typescript/js-specifier/graph.golden.json +++ b/projectgraph/testdata/typescript/js-specifier/graph.golden.json @@ -82,7 +82,7 @@ "src/util.ts": "indexed" }, "stats": { - "version": "14", + "version": "15", "files": 2, "nodes": 3, "edges": 2, diff --git a/projectgraph/testdata/typescript/tsconfig-paths/graph.golden.json b/projectgraph/testdata/typescript/tsconfig-paths/graph.golden.json index 9f28d7e6..09668d30 100644 --- a/projectgraph/testdata/typescript/tsconfig-paths/graph.golden.json +++ b/projectgraph/testdata/typescript/tsconfig-paths/graph.golden.json @@ -138,7 +138,7 @@ "src/main.ts": "indexed" }, "stats": { - "version": "14", + "version": "15", "files": 3, "nodes": 5, "edges": 4, diff --git a/projectgraph/testdata/typescript/type-only/graph.golden.json b/projectgraph/testdata/typescript/type-only/graph.golden.json index 19f90df6..96859381 100644 --- a/projectgraph/testdata/typescript/type-only/graph.golden.json +++ b/projectgraph/testdata/typescript/type-only/graph.golden.json @@ -97,7 +97,7 @@ "src/types.ts": "indexed" }, "stats": { - "version": "14", + "version": "15", "files": 3, "nodes": 5, "edges": 2, diff --git a/test/projectgraph/cases/go/dependency-build-files/summaries/go_github.com_acme_kit_v1.0.0.json b/test/projectgraph/cases/go/dependency-build-files/summaries/go_github.com_acme_kit_v1.0.0.json index f94ff60a..ce73005d 100644 --- a/test/projectgraph/cases/go/dependency-build-files/summaries/go_github.com_acme_kit_v1.0.0.json +++ b/test/projectgraph/cases/go/dependency-build-files/summaries/go_github.com_acme_kit_v1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "14", - "link_version": "14", + "link_version": "15", "unit": "go:github.com/acme/kit@v1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/go/dependency-generated-code/summaries/go_github.com_acme_kit_v1.0.0.json b/test/projectgraph/cases/go/dependency-generated-code/summaries/go_github.com_acme_kit_v1.0.0.json index 4576755e..bbd4b747 100644 --- a/test/projectgraph/cases/go/dependency-generated-code/summaries/go_github.com_acme_kit_v1.0.0.json +++ b/test/projectgraph/cases/go/dependency-generated-code/summaries/go_github.com_acme_kit_v1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "14", - "link_version": "14", + "link_version": "15", "unit": "go:github.com/acme/kit@v1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/go/dependency-implements-interface/summaries/go_github.com_acme_dep_v1.0.0.json b/test/projectgraph/cases/go/dependency-implements-interface/summaries/go_github.com_acme_dep_v1.0.0.json index c1ed7457..3e1ed233 100644 --- a/test/projectgraph/cases/go/dependency-implements-interface/summaries/go_github.com_acme_dep_v1.0.0.json +++ b/test/projectgraph/cases/go/dependency-implements-interface/summaries/go_github.com_acme_dep_v1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "14", - "link_version": "14", + "link_version": "15", "unit": "go:github.com/acme/dep@v1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/go/dependency-internal-call/expect.yaml b/test/projectgraph/cases/go/dependency-internal-call/expect.yaml new file mode 100644 index 00000000..5aff8146 --- /dev/null +++ b/test/projectgraph/cases/go/dependency-internal-call/expect.yaml @@ -0,0 +1,69 @@ +source: safedep:hand +description: >- + main calls Serve of the unit github.com/acme/kit. Serve calls the method readFrame of an unexported type, and + readFrame calls the unexported parseHeader. No file of the project names parseHeader, so the graph has no + external node for it. The target github.com//acme//kit//parseHeader matches the definition of parseHeader in the + unit by its external key, so reach finds the path through Serve and readFrame. Without the match the answer was + no-such-target, and with a reference from unreached project code it was not-found. Dead is called only from the + unexported dead, which nothing calls, so it is not-found. +units: + gomod/github.com/acme/kit@v1.0.0: go:github.com/acme/kit@v1.0.0 +roots: + - key: main.go + kind: main + rule: go.main + - key: main.go//main + kind: main + rule: go.main +unresolved: [] +results: + - target: github.com//acme//kit//parseHeader + status: found + path: + - key: main.go//main + - key: github.com//acme//kit//Serve + site: main.go:6 + kind: direct + confidence: high + - key: go:github.com/acme/kit@v1.0.0//kit.go//Serve + site: go:github.com/acme/kit@v1.0.0//kit.go:3 + kind: package + confidence: high + - key: go:github.com/acme/kit@v1.0.0//kit.go//conn//readFrame + site: go:github.com/acme/kit@v1.0.0//kit.go:5 + kind: member + confidence: high + - key: go:github.com/acme/kit@v1.0.0//kit.go//parseHeader + site: go:github.com/acme/kit@v1.0.0//kit.go:10 + kind: direct + confidence: high + call_sites: + - caller: go:github.com/acme/kit@v1.0.0//kit.go//conn//readFrame + site: go:github.com/acme/kit@v1.0.0//kit.go:10 + reached: true + - target: github.com//acme//kit//conn//readFrame + status: found + path: + - key: main.go//main + - key: github.com//acme//kit//Serve + site: main.go:6 + kind: direct + confidence: high + - key: go:github.com/acme/kit@v1.0.0//kit.go//Serve + site: go:github.com/acme/kit@v1.0.0//kit.go:3 + kind: package + confidence: high + - key: go:github.com/acme/kit@v1.0.0//kit.go//conn//readFrame + site: go:github.com/acme/kit@v1.0.0//kit.go:5 + kind: member + confidence: high + call_sites: + - caller: go:github.com/acme/kit@v1.0.0//kit.go//Serve + site: go:github.com/acme/kit@v1.0.0//kit.go:5 + reached: true + - target: github.com//acme//kit//Dead + status: not-found + call_sites: + - caller: go:github.com/acme/kit@v1.0.0//kit.go//dead + site: go:github.com/acme/kit@v1.0.0//kit.go:16 + reached: false \ No newline at end of file diff --git a/test/projectgraph/cases/go/dependency-internal-call/project/go.mod b/test/projectgraph/cases/go/dependency-internal-call/project/go.mod new file mode 100644 index 00000000..c7090c9c --- /dev/null +++ b/test/projectgraph/cases/go/dependency-internal-call/project/go.mod @@ -0,0 +1,5 @@ +module app + +go 1.22 + +require github.com/acme/kit v1.0.0 diff --git a/test/projectgraph/cases/go/dependency-internal-call/project/gomod/github.com/acme/kit@v1.0.0/go.mod b/test/projectgraph/cases/go/dependency-internal-call/project/gomod/github.com/acme/kit@v1.0.0/go.mod new file mode 100644 index 00000000..529dd612 --- /dev/null +++ b/test/projectgraph/cases/go/dependency-internal-call/project/gomod/github.com/acme/kit@v1.0.0/go.mod @@ -0,0 +1,3 @@ +module github.com/acme/kit + +go 1.22 diff --git a/test/projectgraph/cases/go/dependency-internal-call/project/gomod/github.com/acme/kit@v1.0.0/kit.go b/test/projectgraph/cases/go/dependency-internal-call/project/gomod/github.com/acme/kit@v1.0.0/kit.go new file mode 100644 index 00000000..b825a79c --- /dev/null +++ b/test/projectgraph/cases/go/dependency-internal-call/project/gomod/github.com/acme/kit@v1.0.0/kit.go @@ -0,0 +1,16 @@ +package kit + +func Serve() { + var c conn + c.readFrame() +} + +type conn struct{} + +func (c *conn) readFrame() { parseHeader() } + +func parseHeader() {} + +func Dead() {} + +func dead() { Dead() } diff --git a/test/projectgraph/cases/go/dependency-internal-call/project/main.go b/test/projectgraph/cases/go/dependency-internal-call/project/main.go new file mode 100644 index 00000000..5210efdf --- /dev/null +++ b/test/projectgraph/cases/go/dependency-internal-call/project/main.go @@ -0,0 +1,7 @@ +package main + +import "github.com/acme/kit" + +func main() { + kit.Serve() +} diff --git a/test/projectgraph/cases/go/dependency-internal-call/summaries/go_github.com_acme_kit_v1.0.0.json b/test/projectgraph/cases/go/dependency-internal-call/summaries/go_github.com_acme_kit_v1.0.0.json new file mode 100644 index 00000000..02b64946 --- /dev/null +++ b/test/projectgraph/cases/go/dependency-internal-call/summaries/go_github.com_acme_kit_v1.0.0.json @@ -0,0 +1,23 @@ +{ + "version": "2", + "index_version": "14", + "link_version": "15", + "unit": "go:github.com/acme/kit@v1.0.0", + "symbols": [ + { + "key": "github.com//acme//kit//Dead", + "definitions": [ + "go:github.com/acme/kit@v1.0.0//kit.go//Dead" + ], + "reaches": [] + }, + { + "key": "github.com//acme//kit//Serve", + "definitions": [ + "go:github.com/acme/kit@v1.0.0//kit.go//Serve" + ], + "reaches": [] + } + ], + "targets": [] +} diff --git a/test/projectgraph/cases/go/dependency-module/summaries/go_github.com_acme_yamlish_v1.2.0.json b/test/projectgraph/cases/go/dependency-module/summaries/go_github.com_acme_yamlish_v1.2.0.json index 8ffe98a8..c37f2c5c 100644 --- a/test/projectgraph/cases/go/dependency-module/summaries/go_github.com_acme_yamlish_v1.2.0.json +++ b/test/projectgraph/cases/go/dependency-module/summaries/go_github.com_acme_yamlish_v1.2.0.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "14", - "link_version": "14", + "link_version": "15", "unit": "go:github.com/acme/yamlish@v1.2.0", "symbols": [ { diff --git a/test/projectgraph/cases/go/dependency-platform-files/summaries/go_github.com_acme_kit_v1.0.0.json b/test/projectgraph/cases/go/dependency-platform-files/summaries/go_github.com_acme_kit_v1.0.0.json index 4576755e..bbd4b747 100644 --- a/test/projectgraph/cases/go/dependency-platform-files/summaries/go_github.com_acme_kit_v1.0.0.json +++ b/test/projectgraph/cases/go/dependency-platform-files/summaries/go_github.com_acme_kit_v1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "14", - "link_version": "14", + "link_version": "15", "unit": "go:github.com/acme/kit@v1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/go/dependency-result-types/summaries/go_example.com_markup_v1.0.0.json b/test/projectgraph/cases/go/dependency-result-types/summaries/go_example.com_markup_v1.0.0.json index a073ae94..8fb94de6 100644 --- a/test/projectgraph/cases/go/dependency-result-types/summaries/go_example.com_markup_v1.0.0.json +++ b/test/projectgraph/cases/go/dependency-result-types/summaries/go_example.com_markup_v1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "14", - "link_version": "14", + "link_version": "15", "unit": "go:example.com/markup@v1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/go/dependency-result-types/summaries/go_std_go1.25.1.json b/test/projectgraph/cases/go/dependency-result-types/summaries/go_std_go1.25.1.json index be4042bc..ed174cb8 100644 --- a/test/projectgraph/cases/go/dependency-result-types/summaries/go_std_go1.25.1.json +++ b/test/projectgraph/cases/go/dependency-result-types/summaries/go_std_go1.25.1.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "14", - "link_version": "14", + "link_version": "15", "unit": "go:std@go1.25.1", "symbols": [ { diff --git a/test/projectgraph/cases/javascript/dependency-candidates/summaries/npm_store-kit_1.0.0.json b/test/projectgraph/cases/javascript/dependency-candidates/summaries/npm_store-kit_1.0.0.json index 7b5c6f59..df3b02f5 100644 --- a/test/projectgraph/cases/javascript/dependency-candidates/summaries/npm_store-kit_1.0.0.json +++ b/test/projectgraph/cases/javascript/dependency-candidates/summaries/npm_store-kit_1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "14", - "link_version": "14", + "link_version": "15", "unit": "npm:store-kit@1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/javascript/dependency-exports-import/summaries/npm_kit_1.0.0.json b/test/projectgraph/cases/javascript/dependency-exports-import/summaries/npm_kit_1.0.0.json index df5024b7..22e1431e 100644 --- a/test/projectgraph/cases/javascript/dependency-exports-import/summaries/npm_kit_1.0.0.json +++ b/test/projectgraph/cases/javascript/dependency-exports-import/summaries/npm_kit_1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "14", - "link_version": "14", + "link_version": "15", "unit": "npm:kit@1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/javascript/dependency-exports-require/summaries/npm_kit_1.0.0.json b/test/projectgraph/cases/javascript/dependency-exports-require/summaries/npm_kit_1.0.0.json index df5024b7..22e1431e 100644 --- a/test/projectgraph/cases/javascript/dependency-exports-require/summaries/npm_kit_1.0.0.json +++ b/test/projectgraph/cases/javascript/dependency-exports-require/summaries/npm_kit_1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "14", - "link_version": "14", + "link_version": "15", "unit": "npm:kit@1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/javascript/dependency-method-name/summaries/npm_plug_1.0.0.json b/test/projectgraph/cases/javascript/dependency-method-name/summaries/npm_plug_1.0.0.json index a68af1f0..e7f84498 100644 --- a/test/projectgraph/cases/javascript/dependency-method-name/summaries/npm_plug_1.0.0.json +++ b/test/projectgraph/cases/javascript/dependency-method-name/summaries/npm_plug_1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "14", - "link_version": "14", + "link_version": "15", "unit": "npm:plug@1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/javascript/dependency-same-path/summaries/npm_util-kit_1.0.0.json b/test/projectgraph/cases/javascript/dependency-same-path/summaries/npm_util-kit_1.0.0.json index 0eb4e9be..dfede16a 100644 --- a/test/projectgraph/cases/javascript/dependency-same-path/summaries/npm_util-kit_1.0.0.json +++ b/test/projectgraph/cases/javascript/dependency-same-path/summaries/npm_util-kit_1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "14", - "link_version": "14", + "link_version": "15", "unit": "npm:util-kit@1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/javascript/dependency-transitive/summaries/npm_lodash._reinterpolate_3.0.0.json b/test/projectgraph/cases/javascript/dependency-transitive/summaries/npm_lodash._reinterpolate_3.0.0.json index 7bf0531b..cf89d8cf 100644 --- a/test/projectgraph/cases/javascript/dependency-transitive/summaries/npm_lodash._reinterpolate_3.0.0.json +++ b/test/projectgraph/cases/javascript/dependency-transitive/summaries/npm_lodash._reinterpolate_3.0.0.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "14", - "link_version": "14", + "link_version": "15", "unit": "npm:lodash._reinterpolate@3.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/javascript/dependency-transitive/summaries/npm_lodash_4.17.21.json b/test/projectgraph/cases/javascript/dependency-transitive/summaries/npm_lodash_4.17.21.json index 89c87485..48a600cf 100644 --- a/test/projectgraph/cases/javascript/dependency-transitive/summaries/npm_lodash_4.17.21.json +++ b/test/projectgraph/cases/javascript/dependency-transitive/summaries/npm_lodash_4.17.21.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "14", - "link_version": "14", + "link_version": "15", "unit": "npm:lodash@4.17.21", "symbols": [ { diff --git a/test/projectgraph/cases/python/dependency-dark-unit/summaries/pypi_PyYAML_6.0.1.json b/test/projectgraph/cases/python/dependency-dark-unit/summaries/pypi_PyYAML_6.0.1.json index e4aa580e..c491bc6a 100644 --- a/test/projectgraph/cases/python/dependency-dark-unit/summaries/pypi_PyYAML_6.0.1.json +++ b/test/projectgraph/cases/python/dependency-dark-unit/summaries/pypi_PyYAML_6.0.1.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "14", - "link_version": "14", + "link_version": "15", "unit": "pypi:PyYAML@6.0.1", "symbols": [ { diff --git a/test/projectgraph/cases/python/dependency-failed-reached/summaries/pypi_badpkg_1.0.0.json b/test/projectgraph/cases/python/dependency-failed-reached/summaries/pypi_badpkg_1.0.0.json index 8b519218..9d6cece7 100644 --- a/test/projectgraph/cases/python/dependency-failed-reached/summaries/pypi_badpkg_1.0.0.json +++ b/test/projectgraph/cases/python/dependency-failed-reached/summaries/pypi_badpkg_1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "14", - "link_version": "14", + "link_version": "15", "unit": "pypi:badpkg@1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/python/dependency-failed-reached/summaries/pypi_okpkg_1.0.0.json b/test/projectgraph/cases/python/dependency-failed-reached/summaries/pypi_okpkg_1.0.0.json index 464b2d14..62618129 100644 --- a/test/projectgraph/cases/python/dependency-failed-reached/summaries/pypi_okpkg_1.0.0.json +++ b/test/projectgraph/cases/python/dependency-failed-reached/summaries/pypi_okpkg_1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "14", - "link_version": "14", + "link_version": "15", "unit": "pypi:okpkg@1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/python/dependency-failed-unreached/summaries/pypi_badpkg_1.0.0.json b/test/projectgraph/cases/python/dependency-failed-unreached/summaries/pypi_badpkg_1.0.0.json index 8b519218..9d6cece7 100644 --- a/test/projectgraph/cases/python/dependency-failed-unreached/summaries/pypi_badpkg_1.0.0.json +++ b/test/projectgraph/cases/python/dependency-failed-unreached/summaries/pypi_badpkg_1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "14", - "link_version": "14", + "link_version": "15", "unit": "pypi:badpkg@1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/python/dependency-failed-unreached/summaries/pypi_okpkg_1.0.0.json b/test/projectgraph/cases/python/dependency-failed-unreached/summaries/pypi_okpkg_1.0.0.json index 464b2d14..62618129 100644 --- a/test/projectgraph/cases/python/dependency-failed-unreached/summaries/pypi_okpkg_1.0.0.json +++ b/test/projectgraph/cases/python/dependency-failed-unreached/summaries/pypi_okpkg_1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "14", - "link_version": "14", + "link_version": "15", "unit": "pypi:okpkg@1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/python/dependency-package-dir/summaries/pypi_outer_1.0.0.json b/test/projectgraph/cases/python/dependency-package-dir/summaries/pypi_outer_1.0.0.json index 98cbddc4..6f891293 100644 --- a/test/projectgraph/cases/python/dependency-package-dir/summaries/pypi_outer_1.0.0.json +++ b/test/projectgraph/cases/python/dependency-package-dir/summaries/pypi_outer_1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "14", - "link_version": "14", + "link_version": "15", "unit": "pypi:outer@1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/python/dependency-site-packages/summaries/pypi_requests_2.31.0.json b/test/projectgraph/cases/python/dependency-site-packages/summaries/pypi_requests_2.31.0.json index bb94bfd5..ecccd43b 100644 --- a/test/projectgraph/cases/python/dependency-site-packages/summaries/pypi_requests_2.31.0.json +++ b/test/projectgraph/cases/python/dependency-site-packages/summaries/pypi_requests_2.31.0.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "14", - "link_version": "14", + "link_version": "15", "unit": "pypi:requests@2.31.0", "symbols": [ { diff --git a/test/projectgraph/cases/python/dependency-site-packages/summaries/pypi_urllib3_2.0.7.json b/test/projectgraph/cases/python/dependency-site-packages/summaries/pypi_urllib3_2.0.7.json index bcc32e77..47a3bc13 100644 --- a/test/projectgraph/cases/python/dependency-site-packages/summaries/pypi_urllib3_2.0.7.json +++ b/test/projectgraph/cases/python/dependency-site-packages/summaries/pypi_urllib3_2.0.7.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "14", - "link_version": "14", + "link_version": "15", "unit": "pypi:urllib3@2.0.7", "symbols": [ { diff --git a/test/projectgraph/cases/rust/dependency-crate/summaries/cargo_kit_1.0.0.json b/test/projectgraph/cases/rust/dependency-crate/summaries/cargo_kit_1.0.0.json index 19ab23ab..3b7060db 100644 --- a/test/projectgraph/cases/rust/dependency-crate/summaries/cargo_kit_1.0.0.json +++ b/test/projectgraph/cases/rust/dependency-crate/summaries/cargo_kit_1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "14", - "link_version": "14", + "link_version": "15", "unit": "cargo:kit@1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/rust/dependency-internal-call/expect.yaml b/test/projectgraph/cases/rust/dependency-internal-call/expect.yaml new file mode 100644 index 00000000..151d8727 --- /dev/null +++ b/test/projectgraph/cases/rust/dependency-internal-call/expect.yaml @@ -0,0 +1,36 @@ +source: safedep:hand +description: >- + main calls serve of the crate kit, the unit cargo:kit@1.0.0. serve calls the private parse_header. No file of the + project names parse_header, so the graph has no external node for it. The target kit//parse_header matches the + definition in the unit by its external key, so reach finds the path through serve. +units: + vendor/kit: cargo:kit@1.0.0 +roots: + - key: src/main.rs + kind: main + rule: rust.main + - key: src/main.rs//main + kind: main + rule: rust.main +unresolved: [] +results: + - target: kit//parse_header + status: found + path: + - key: src/main.rs//main + - key: kit//serve + site: src/main.rs:2 + kind: direct + confidence: high + - key: cargo:kit@1.0.0//src/lib.rs//serve + site: cargo:kit@1.0.0//src/lib.rs:1 + kind: package + confidence: high + - key: cargo:kit@1.0.0//src/lib.rs//parse_header + site: cargo:kit@1.0.0//src/lib.rs:2 + kind: direct + confidence: high + call_sites: + - caller: cargo:kit@1.0.0//src/lib.rs//serve + site: cargo:kit@1.0.0//src/lib.rs:2 + reached: true \ No newline at end of file diff --git a/test/projectgraph/cases/rust/dependency-internal-call/project/Cargo.toml b/test/projectgraph/cases/rust/dependency-internal-call/project/Cargo.toml new file mode 100644 index 00000000..492d638f --- /dev/null +++ b/test/projectgraph/cases/rust/dependency-internal-call/project/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "app" +version = "0.1.0" +edition = "2021" + +[dependencies] +kit = "1.0.0" diff --git a/test/projectgraph/cases/rust/dependency-internal-call/project/src/main.rs b/test/projectgraph/cases/rust/dependency-internal-call/project/src/main.rs new file mode 100644 index 00000000..0e24e669 --- /dev/null +++ b/test/projectgraph/cases/rust/dependency-internal-call/project/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + kit::serve(); +} diff --git a/test/projectgraph/cases/rust/dependency-internal-call/project/vendor/kit/Cargo.toml b/test/projectgraph/cases/rust/dependency-internal-call/project/vendor/kit/Cargo.toml new file mode 100644 index 00000000..58da5f79 --- /dev/null +++ b/test/projectgraph/cases/rust/dependency-internal-call/project/vendor/kit/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "kit" +version = "1.0.0" +edition = "2021" diff --git a/test/projectgraph/cases/rust/dependency-internal-call/project/vendor/kit/src/lib.rs b/test/projectgraph/cases/rust/dependency-internal-call/project/vendor/kit/src/lib.rs new file mode 100644 index 00000000..7746db0b --- /dev/null +++ b/test/projectgraph/cases/rust/dependency-internal-call/project/vendor/kit/src/lib.rs @@ -0,0 +1,5 @@ +pub fn serve() { + parse_header(); +} + +fn parse_header() {} diff --git a/test/projectgraph/cases/rust/dependency-internal-call/summaries/cargo_kit_1.0.0.json b/test/projectgraph/cases/rust/dependency-internal-call/summaries/cargo_kit_1.0.0.json new file mode 100644 index 00000000..56a598a7 --- /dev/null +++ b/test/projectgraph/cases/rust/dependency-internal-call/summaries/cargo_kit_1.0.0.json @@ -0,0 +1,16 @@ +{ + "version": "2", + "index_version": "14", + "link_version": "15", + "unit": "cargo:kit@1.0.0", + "symbols": [ + { + "key": "kit//serve", + "definitions": [ + "cargo:kit@1.0.0//src/lib.rs//serve" + ], + "reaches": [] + } + ], + "targets": [] +} diff --git a/test/projectgraph/cases/rust/library-build-script/expect.yaml b/test/projectgraph/cases/rust/library-build-script/expect.yaml new file mode 100644 index 00000000..30000d38 --- /dev/null +++ b/test/projectgraph/cases/rust/library-build-script/expect.yaml @@ -0,0 +1,40 @@ +source: safedep:hand +description: >- + A library crate with a build script. build.rs has a main, and it stays a root. The build script is a helper + program, and the users of the crate call its public items, so the public generate of src/lib.rs is a root of kind + library-surface too. generate calls kit::fill of the unit cargo:kit@1.0.0, so reach finds it. When the build + script turned off the library-surface roots, the answer was not-found. +units: + vendor/kit: cargo:kit@1.0.0 +roots: + - key: build.rs + kind: library-surface + rule: library-surface + - key: build.rs + kind: main + rule: rust.build-script + - key: build.rs//main + kind: main + rule: rust.main + - key: src/lib.rs + kind: library-surface + rule: library-surface + - key: src/lib.rs//generate + kind: library-surface + rule: library-surface +unresolved: [] +results: + - target: kit//fill + status: found + path: + - key: src/lib.rs//generate + - key: kit//fill + site: src/lib.rs:2 + kind: direct + confidence: high + call_sites: + - caller: src/lib.rs//generate + site: src/lib.rs:2 + reached: true + uncertainties: + - kind: root-fallback diff --git a/test/projectgraph/cases/rust/library-build-script/project/Cargo.toml b/test/projectgraph/cases/rust/library-build-script/project/Cargo.toml new file mode 100644 index 00000000..0b195ea2 --- /dev/null +++ b/test/projectgraph/cases/rust/library-build-script/project/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "lib" +version = "0.1.0" +edition = "2021" + +[dependencies] +kit = "1.0.0" diff --git a/test/projectgraph/cases/rust/library-build-script/project/build.rs b/test/projectgraph/cases/rust/library-build-script/project/build.rs new file mode 100644 index 00000000..c2bce4fe --- /dev/null +++ b/test/projectgraph/cases/rust/library-build-script/project/build.rs @@ -0,0 +1,3 @@ +fn main() { + println!("cargo:rerun-if-changed=build.rs"); +} diff --git a/test/projectgraph/cases/rust/library-build-script/project/src/lib.rs b/test/projectgraph/cases/rust/library-build-script/project/src/lib.rs new file mode 100644 index 00000000..4c8c7a62 --- /dev/null +++ b/test/projectgraph/cases/rust/library-build-script/project/src/lib.rs @@ -0,0 +1,3 @@ +pub fn generate() { + kit::fill(); +} diff --git a/test/projectgraph/cases/rust/library-build-script/project/vendor/kit/Cargo.toml b/test/projectgraph/cases/rust/library-build-script/project/vendor/kit/Cargo.toml new file mode 100644 index 00000000..58da5f79 --- /dev/null +++ b/test/projectgraph/cases/rust/library-build-script/project/vendor/kit/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "kit" +version = "1.0.0" +edition = "2021" diff --git a/test/projectgraph/cases/rust/library-build-script/project/vendor/kit/src/lib.rs b/test/projectgraph/cases/rust/library-build-script/project/vendor/kit/src/lib.rs new file mode 100644 index 00000000..f0212044 --- /dev/null +++ b/test/projectgraph/cases/rust/library-build-script/project/vendor/kit/src/lib.rs @@ -0,0 +1 @@ +pub fn fill() {} diff --git a/test/projectgraph/cases/rust/library-build-script/summaries/cargo_kit_1.0.0.json b/test/projectgraph/cases/rust/library-build-script/summaries/cargo_kit_1.0.0.json new file mode 100644 index 00000000..90a2c656 --- /dev/null +++ b/test/projectgraph/cases/rust/library-build-script/summaries/cargo_kit_1.0.0.json @@ -0,0 +1,16 @@ +{ + "version": "2", + "index_version": "14", + "link_version": "15", + "unit": "cargo:kit@1.0.0", + "symbols": [ + { + "key": "kit//fill", + "definitions": [ + "cargo:kit@1.0.0//src/lib.rs//fill" + ], + "reaches": [] + } + ], + "targets": [] +} diff --git a/test/projectgraph/report.md b/test/projectgraph/report.md index 24966c46..805e59f1 100644 --- a/test/projectgraph/report.md +++ b/test/projectgraph/report.md @@ -1006,3 +1006,30 @@ inside a unit. More reached code of the units gives more name matches on more code, and it is not a measure of the receiver forms. The receiver table of FOU-542 measures those, and every row of it is found. +After FOU-555: + +| Language | Kind | Targets | found | not-found | no-such-target | unknown-receiver | no-definition | interface-receiver | no-dependency-source | budget | ambiguous | computed-name | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| Go | function | 256 | 51 | 0 | 0 | 3 | 5 | 0 | 2 | 39 | 106 | 50 | +| Go | method | 1298 | 331 | 0 | 0 | 154 | 71 | 2 | 2 | 109 | 416 | 213 | +| Rust | function | 13 | 4 | 0 | 0 | 0 | 9 | 0 | 0 | 0 | 0 | 0 | +| Rust | method | 38 | 16 | 0 | 0 | 0 | 13 | 0 | 0 | 9 | 0 | 0 | +| Rust | other | 5 | 0 | 0 | 5 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | + +- A target now matches each definition of a unit whose external key is the + target, so a target that only the code of its unit calls is found + through the definition. `found` moves from 30 to 331 Go method targets, + from 13 to 51 Go function targets, and from 0 to 16 Rust method targets. + The share of Go method targets that end as `unknown-receiver` falls from + 0.247 to 0.119, and no target is `no-such-target` any more for Go. +- The match first gave 8 Rust targets as `not-found`. cookie calls + `rand::thread_rng` from its public `Key::generate`, but its `build.rs` + was the only root, so the library-surface roots were off. A build + script, an example or a benchmark of a Rust crate is now a helper + program. Its root stays, and it does not turn off the library-surface + roots. No target of the measure is `not-found` now. +- cookie reaches `rand::thread_rng` through the code of rand, not through + its own call `crate::secure::rand::thread_rng()`. The link step does not + read `extern crate rand;` in a submodule. The path is longer, and the + answer is the same. + diff --git a/test/projectgraph/version_test.go b/test/projectgraph/version_test.go index a1e20c4b..10054342 100644 --- a/test/projectgraph/version_test.go +++ b/test/projectgraph/version_test.go @@ -29,6 +29,7 @@ var graphHashes = map[string]string{ "12/12": "1cb170ec83fe3517e6f71755983c040210f83bb7a0e69528b3d7007dcc69d5dc", "13/13": "9383a0bc5c6e8b760beb443112cae1400c39212c1da5c2d155c22488eb00eaa6", "14/14": "0f3c9c38b39f4de1835f34e8a58bcea85461f37e7aa2d5a2dd484569e670ff48", + "14/15": "bbc708aba767ccf554c75a7e06816abda3fc1804ad6d0876ad8ce9575f4c3a88", } // resultHashes records the hash of the reach results of every case for @@ -42,6 +43,7 @@ var resultHashes = map[string]string{ "12/12/2": "b89a142078d885eeb73fe15c731315c60d42b38270efd12a7857be44b9ccc83c", "13/13/2": "2f81b00e1708cbb7e0589f34eaa7866a5658a6cfc0a7608d532381ca4267ba06", "14/14/2": "a832ea709bf3df852814375a56e6329c35e2ff4c49ae34ee49afe042e9502d17", + "14/15/3": "77dfd0ea8ab71869ef612d41b8c2e46852d4fb67a49458cb645321692af77ea0", } // summaryHashes records the hash of the golden summaries of the cases for @@ -56,6 +58,7 @@ var summaryHashes = map[string]string{ "12/12/2": "cb2147fda018110f38254519bbc75a814b99e0318f5c7485fbce78a667bb4287", "13/13/2": "dc844b1cd13e7873f54d646f27384c78b64707e13ae9c9ef27a4557ba7ba4dfc", "14/14/2": "27424c4e2525450479faba78421959738748811d70b9a93b81e2bbdf1733bf0d", + "14/15/2": "5969f3cda15b666cbd902bbc2db953cbdef8c0ce48d5e77446cf243cdd0bb0e7", } func TestGraphHashFollowsVersion(t *testing.T) { From 1b83276f24cf2d1b5252aba20fea8235346bbd37 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 19 Oct 2026 10:16:53 +0000 Subject: [PATCH 8/9] fix(projectgraph): read Cargo renames and stop at platform result types A Cargo.toml can rename a dependency, as helper = { package = "kit" }. A path of the crate writes helper, and the unit has the crate kit, so no package edge reached the unit and a target of kit was not-found. The link step now reads the renames and gives each origin the module of the package. Two platform files of a Go package can define one function with two result types. The walk took the type of the first file, so a method of the other type was not-found. A result is now known only when every definition and its twins give the same type. Else the call is a record with the reason unknown-receiver. projectgraph.Version is 16. New cases: rust/dependency-renamed and go/dependency-platform-results. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01PFQbqPqd9xn1fT6hBsyoUm Signed-off-by: Claude --- docs/projectgraph.md | 6 ++ projectgraph/graph.go | 2 +- projectgraph/imports.go | 2 +- projectgraph/modules/cargo.go | 62 +++++++++++++++++-- projectgraph/modules/cargo_test.go | 19 ++++++ projectgraph/packages.go | 11 +++- projectgraph/profile.go | 4 ++ projectgraph/results.go | 42 +++++++++---- projectgraph/rust.go | 17 +++++ .../javascript/barrel-chain/graph.golden.json | 2 +- .../commonjs-require/graph.golden.json | 2 +- .../default-import/graph.golden.json | 2 +- .../esm-named-import/graph.golden.json | 2 +- .../javascript/export-star/graph.golden.json | 2 +- .../exports-conditions/graph.golden.json | 2 +- .../unknown-receiver/graph.golden.json | 2 +- .../javascript/workspaces/graph.golden.json | 2 +- .../python/all-hides/graph.golden.json | 2 +- .../python/assignment/graph.golden.json | 2 +- .../python/class-body/graph.golden.json | 2 +- .../python/decorators/graph.golden.json | 2 +- .../python/external/graph.golden.json | 2 +- .../python/local-receiver/graph.golden.json | 2 +- .../python/member-call/graph.golden.json | 2 +- .../python/missing-file/graph.golden.json | 2 +- .../python/package-init/graph.golden.json | 2 +- .../python/reexport-cycle/graph.golden.json | 2 +- .../python/relative-imports/graph.golden.json | 2 +- .../python/sibling-import/graph.golden.json | 2 +- .../python/src-layout/graph.golden.json | 2 +- .../python/sys-path/graph.golden.json | 2 +- .../python/wildcard/graph.golden.json | 2 +- .../roots/extra-rules/graph.golden.json | 2 +- .../roots/library-surface/graph.golden.json | 2 +- .../roots/main-guard/graph.golden.json | 2 +- .../roots/package-json-bin/graph.golden.json | 2 +- .../roots/package-json-main/graph.golden.json | 2 +- .../procfile-dockerfile/graph.golden.json | 2 +- .../roots/pyproject-scripts/graph.golden.json | 2 +- .../testdata/roots/setup-py/graph.golden.json | 2 +- .../roots/test-file/graph.golden.json | 2 +- .../typescript/js-specifier/graph.golden.json | 2 +- .../tsconfig-paths/graph.golden.json | 2 +- .../typescript/type-only/graph.golden.json | 2 +- .../go_github.com_acme_kit_v1.0.0.json | 2 +- .../go_github.com_acme_kit_v1.0.0.json | 2 +- .../go_github.com_acme_dep_v1.0.0.json | 2 +- .../go_github.com_acme_kit_v1.0.0.json | 2 +- .../go_github.com_acme_yamlish_v1.2.0.json | 2 +- .../go_github.com_acme_kit_v1.0.0.json | 2 +- .../dependency-platform-results/expect.yaml | 37 +++++++++++ .../project/go.mod | 5 ++ .../gomod/github.com/acme/kit@v1.0.0/go.mod | 3 + .../github.com/acme/kit@v1.0.0/new_linux.go | 7 +++ .../github.com/acme/kit@v1.0.0/new_windows.go | 9 +++ .../project/main.go | 7 +++ .../go_github.com_acme_kit_v1.0.0.json | 51 +++++++++++++++ .../go_example.com_markup_v1.0.0.json | 2 +- .../summaries/go_std_go1.25.1.json | 2 +- .../summaries/npm_store-kit_1.0.0.json | 2 +- .../summaries/npm_kit_1.0.0.json | 2 +- .../summaries/npm_kit_1.0.0.json | 2 +- .../summaries/npm_plug_1.0.0.json | 2 +- .../summaries/npm_util-kit_1.0.0.json | 2 +- .../npm_lodash._reinterpolate_3.0.0.json | 2 +- .../summaries/npm_lodash_4.17.21.json | 2 +- .../summaries/pypi_PyYAML_6.0.1.json | 2 +- .../summaries/pypi_badpkg_1.0.0.json | 2 +- .../summaries/pypi_okpkg_1.0.0.json | 2 +- .../summaries/pypi_badpkg_1.0.0.json | 2 +- .../summaries/pypi_okpkg_1.0.0.json | 2 +- .../summaries/pypi_outer_1.0.0.json | 2 +- .../summaries/pypi_requests_2.31.0.json | 2 +- .../summaries/pypi_urllib3_2.0.7.json | 2 +- .../summaries/cargo_kit_1.0.0.json | 2 +- .../summaries/cargo_kit_1.0.0.json | 2 +- .../cases/rust/dependency-renamed/expect.yaml | 36 +++++++++++ .../dependency-renamed/project/Cargo.toml | 7 +++ .../dependency-renamed/project/src/main.rs | 3 + .../project/vendor/kit/Cargo.toml | 4 ++ .../project/vendor/kit/src/lib.rs | 5 ++ .../summaries/cargo_kit_1.0.0.json | 16 +++++ .../summaries/cargo_kit_1.0.0.json | 2 +- test/projectgraph/version_test.go | 3 + 84 files changed, 399 insertions(+), 81 deletions(-) create mode 100644 test/projectgraph/cases/go/dependency-platform-results/expect.yaml create mode 100644 test/projectgraph/cases/go/dependency-platform-results/project/go.mod create mode 100644 test/projectgraph/cases/go/dependency-platform-results/project/gomod/github.com/acme/kit@v1.0.0/go.mod create mode 100644 test/projectgraph/cases/go/dependency-platform-results/project/gomod/github.com/acme/kit@v1.0.0/new_linux.go create mode 100644 test/projectgraph/cases/go/dependency-platform-results/project/gomod/github.com/acme/kit@v1.0.0/new_windows.go create mode 100644 test/projectgraph/cases/go/dependency-platform-results/project/main.go create mode 100644 test/projectgraph/cases/go/dependency-platform-results/summaries/go_github.com_acme_kit_v1.0.0.json create mode 100644 test/projectgraph/cases/rust/dependency-renamed/expect.yaml create mode 100644 test/projectgraph/cases/rust/dependency-renamed/project/Cargo.toml create mode 100644 test/projectgraph/cases/rust/dependency-renamed/project/src/main.rs create mode 100644 test/projectgraph/cases/rust/dependency-renamed/project/vendor/kit/Cargo.toml create mode 100644 test/projectgraph/cases/rust/dependency-renamed/project/vendor/kit/src/lib.rs create mode 100644 test/projectgraph/cases/rust/dependency-renamed/summaries/cargo_kit_1.0.0.json diff --git a/docs/projectgraph.md b/docs/projectgraph.md index ddfb13c6..078744d5 100644 --- a/docs/projectgraph.md +++ b/docs/projectgraph.md @@ -191,6 +191,7 @@ edge joins the key to the definition in the unit. A chained call, as | An interface type | The `cha` edges to the implementations in the graph. An implementation in a unit gets its edge through its external key. With no implementation, the site is a record with the reason `interface-receiver` | | A function of a package that no unit gives | A record with the reason `no-dependency-source` | | A result with no named type, or a function that the unit does not define | A record with the reason `unknown-receiver` | +| Two definitions of the function, as in two platform files of a Go package, with two result types | A record with the reason `unknown-receiver`. The walk follows one type, and a method of the other type would be `not-found` | Rust follows the same rules, with these additions. @@ -200,6 +201,11 @@ Rust follows the same rules, with these additions. an item is the path that another crate writes, and an item of a private module that the root gives on with `pub use` gets the shortest path, as `smallvec//SmallVec`. +- A Cargo.toml can rename a dependency, as `helper = { package = "kit" }`. + A path of the crate writes `helper`, and the link step reads it as the + crate `kit` of the unit. The external node keeps the written name, as + `helper//serve`, and the package edge goes on to the definition in the + unit, whose external key is `kit//serve`. - The index records the return type of each function and method, with `Self` as the type of its `impl`. A type drops `&`, `&mut`, `Box<..>`, `Rc<..>`, `Arc<..>` and its type arguments, so `&mut SmallVec<[u8; 4]>` diff --git a/projectgraph/graph.go b/projectgraph/graph.go index 7f41133b..09f7bd51 100644 --- a/projectgraph/graph.go +++ b/projectgraph/graph.go @@ -17,7 +17,7 @@ import ( // Version is the version of the link rules. Bump it when a rule that makes a // node, an edge, a root or an unresolved site changes, so a diff of two // reports shows the change. -const Version = "15" +const Version = "16" type NodeKind string diff --git a/projectgraph/imports.go b/projectgraph/imports.go index 32c25345..85a930d0 100644 --- a/projectgraph/imports.go +++ b/projectgraph/imports.go @@ -74,7 +74,7 @@ func (b *builder) importedModules(ix *fileindex.Index, spec string, form modules } var mods []moduleRef var missing []string - for _, pv := range b.providers(origin{lang: ix.Language, unit: ix.Unit, module: spec, form: form}) { + for _, pv := range b.providers(origin{lang: ix.Language, unit: ix.Unit, module: b.packageModule(ix, spec), form: form}) { if pv.module == "" { missing = append(missing, pv.sp.unit) continue diff --git a/projectgraph/modules/cargo.go b/projectgraph/modules/cargo.go index 7d6dc85a..c4084706 100644 --- a/projectgraph/modules/cargo.go +++ b/projectgraph/modules/cargo.go @@ -18,10 +18,13 @@ type Cargo struct { problems problems } -// Crate is one package of a Cargo.toml. +// Crate is one package of a Cargo.toml. Renames maps the name that the +// crate writes for a dependency to the name of its package, as helper to +// kit for helper = { package = "kit" }. type Crate struct { - Dir string - Name string + Dir string + Name string + Renames map[string]string } // NewCargo reads the Cargo.toml of each directory of the file set. The name @@ -41,10 +44,11 @@ func NewCargo(files *Files, root fs.FS) *Cargo { c.problems.add(name, err) continue } + pairs := ScanTOML(data) if crate := cargoCrate(data); crate != "" { - c.crates = append(c.crates, Crate{Dir: d, Name: strings.ReplaceAll(crate, "-", "_")}) + c.crates = append(c.crates, Crate{Dir: d, Name: crateName(crate), Renames: cargoRenames(pairs)}) } - for _, pair := range ScanTOML(data) { + for _, pair := range pairs { if name, ok := cargoDependency(pair); ok && !slices.Contains(c.dependencies, name) { c.dependencies = append(c.dependencies, name) } @@ -70,6 +74,43 @@ func cargoCrate(data []byte) string { return cmp.Or(names["lib"], names["package"]) } +// cargoRenames returns the dependencies that a Cargo.toml renames with the +// package key, inline as helper = { package = "kit" } or in a +// [dependencies.helper] table. Both names are as a path writes them. +func cargoRenames(pairs []TOMLPair) map[string]string { + var renames map[string]string + for _, pair := range pairs { + name, ok := cargoDependency(pair) + if !ok { + continue + } + pkg := "" + if inline, ok := TOMLInlineTable(pair.Value); ok && name == pair.Key { + for _, p := range inline { + if p.Key == "package" { + pkg = p.Value + } + } + } else if pair.Key == "package" && name != pair.Key { + pkg, _ = TOMLString(pair.Value) + } + if pkg == "" { + continue + } + if renames == nil { + renames = map[string]string{} + } + renames[crateName(name)] = crateName(pkg) + } + return renames +} + +// crateName returns the name that a path writes for a package, as +// serde_json for serde-json. +func crateName(name string) string { + return strings.ReplaceAll(name, "-", "_") +} + // Problems returns the Cargo.toml files that the reader could not read. func (c *Cargo) Problems() []Problem { return c.problems } @@ -86,6 +127,17 @@ func (c *Cargo) Crate(file string) (Crate, bool) { return Crate{}, false } +// Rename returns the package that a file names by a crate name: the +// package of a rename in the Cargo.toml of the file, or else the name. +func (c *Cargo) Rename(file, name string) string { + if crate, ok := c.Crate(file); ok { + if pkg, ok := crate.Renames[name]; ok { + return pkg + } + } + return name +} + // Has reports whether a crate of the project has the name. func (c *Cargo) Has(name string) bool { return slices.ContainsFunc(c.crates, func(crate Crate) bool { return crate.Name == name }) diff --git a/projectgraph/modules/cargo_test.go b/projectgraph/modules/cargo_test.go index 5404604d..812b8714 100644 --- a/projectgraph/modules/cargo_test.go +++ b/projectgraph/modules/cargo_test.go @@ -32,3 +32,22 @@ func TestCargoReadsTheCratesOfTheManifests(t *testing.T) { assert.False(t, c.Has("web_app")) assert.Empty(t, c.Problems()) } + +func TestCargoReadsTheRenamesOfADependency(t *testing.T) { + root := fstest.MapFS{ + "Cargo.toml": {Data: []byte("[package]\nname = \"app\"\n\n[dependencies]\nhelper = { package = \"kit-core\", version = \"1\" }\nserde = \"1\"\n\n[target.'cfg(unix)'.dependencies.sys]\npackage = \"kit-sys\"\nversion = \"1\"\n")}, + "src/main.rs": {Data: []byte("")}, + } + c := NewCargo(NewFiles([]string{"src/main.rs"}), root) + + for _, tc := range []struct { + name, want string + }{ + {"helper", "kit_core"}, + {"sys", "kit_sys"}, + {"serde", "serde"}, + {"kit_core", "kit_core"}, + } { + assert.Equal(t, tc.want, c.Rename("src/main.rs", tc.name), tc.name) + } +} diff --git a/projectgraph/packages.go b/projectgraph/packages.go index 8b4a1d3b..a84e88d4 100644 --- a/projectgraph/packages.go +++ b/projectgraph/packages.go @@ -231,12 +231,21 @@ func (b *builder) recordOrigin(ix *fileindex.Index, key, module string, form mod if len(b.spaces) < 2 { return } - o := origin{lang: ix.Language, unit: ix.Unit, module: module, form: form, names: slices.Clone(names)} + o := origin{lang: ix.Language, unit: ix.Unit, module: b.packageModule(ix, module), form: form, names: slices.Clone(names)} if !slices.ContainsFunc(b.origins[key], o.equal) { b.origins[key] = append(b.origins[key], o) } } +// packageModule returns the module of the package that an import of the +// file names, by the profile of its family. +func (b *builder) packageModule(ix *fileindex.Index, module string) string { + if imported := profileOf(ix.Language.Family()).importedModule; imported != nil { + return imported(b, ix, module) + } + return module +} + // deriveExternal returns the external node of the names under an external // key. The origins of the key, with the names after their own, are the // origins of the new key. diff --git a/projectgraph/profile.go b/projectgraph/profile.go index a1d52387..eb2579df 100644 --- a/projectgraph/profile.go +++ b/projectgraph/profile.go @@ -47,6 +47,10 @@ type profile struct { // surface adds the exported symbols of a unit of dependency code with // the import that names each one. The profile of the family holds it. surface func(s *surfacer) + // importedModule returns the module of the package that an import of a + // file names, as kit::serve for helper::serve when the Cargo.toml of + // the file renames the package kit to helper. + importedModule func(b *builder, ix *fileindex.Index, module string) string // symbol returns the module that an importer of a definition of a unit // of dependency code imports, and the names of the definition under // it, as golang.org/x/net/html and Tokenizer. The profile of the family diff --git a/projectgraph/results.go b/projectgraph/results.go index 291734a0..ec2e09de 100644 --- a/projectgraph/results.go +++ b/projectgraph/results.go @@ -31,17 +31,40 @@ func (b *builder) resultType(unit, fn string, pos int) (result, fileindex.Unreso return result{}, fileindex.UnresolvedNoDependencySource, false } } + if res, ok := b.oneResult(unit, defs, pos); ok { + return res, "", true + } + return result{}, fileindex.UnresolvedUnknownReceiver, false +} + +// oneResult returns the result at a position that all the definitions and +// their twins give. The platform files of a Go package can define one +// function twice with two result types. The walk follows one type, and a +// method of the other type would then be not-found, so two results give +// none. +func (b *builder) oneResult(unit string, defs []string, pos int) (result, bool) { + var all []string for _, d := range defs { + all = append(append(all, d), b.twins(d)...) + } + var out result + for _, d := range all { def := b.defs[d] text := resultText(def, pos) if text == "" { continue } - if class, ok := b.resolveType(b.scopes[fileindex.Unit(d)], d, text); ok { - return result{class: b.typeFor(unit, class), wraps: def.Wraps}, "", true + class, ok := b.resolveType(b.scopes[fileindex.Unit(d)], d, text) + if !ok { + return result{}, false + } + res := result{class: b.typeFor(unit, class), wraps: def.Wraps} + if out.class != "" && res != out { + return result{}, false } + out = res } - return result{}, fileindex.UnresolvedUnknownReceiver, false + return out, out.class != "" } // unwrap returns how many of the names after a call a wrapped result @@ -119,15 +142,10 @@ func (b *builder) externalResult(parent, name string) (result, bool) { return result{}, false } defs, _ := b.unitDefinitions(origins) - for _, d := range defs { - if !b.isFunction(d) { - continue - } - // The walk reads an external key, so the code that names it is - // in another space than the unit, and the type is its key. - if res, _, ok := b.resultType("", d, 0); ok { - return res, true - } + // The walk reads an external key, so the code that names it is in + // another space than the unit, and the type is its key. + if res, ok := b.oneResult("", slices.DeleteFunc(defs, func(d string) bool { return !b.isFunction(d) }), 0); ok { + return res, true } if profileOf(origins[0].lang).sameTypeMethods[name] { types, _ := b.unitDefinitions(b.origins[parent]) diff --git a/projectgraph/rust.go b/projectgraph/rust.go index 7ddb2e15..aacb25ea 100644 --- a/projectgraph/rust.go +++ b/projectgraph/rust.go @@ -24,6 +24,7 @@ var rustProfile = profile{ link: (*builder).linkRust, scopeFallback: (*builder).rustScopeFallback, provide: rustProvide, + importedModule: rustImportedModule, symbol: rustSymbol, surface: rustSurface, unitTypes: true, @@ -294,6 +295,22 @@ func rustProvide(b *builder, sp *space, module string, _ modules.Form) []provisi return []provision{{sp: sp}} } +// rustImportedModule returns the path of the package that a path of a file +// names. A path writes the name that the Cargo.toml of its crate gives a +// dependency, and the crate of the unit has the name of the package. +func rustImportedModule(b *builder, ix *fileindex.Index, module string) string { + sp := b.spaceOf(ix.Path) + first, rest, nested := strings.Cut(strings.TrimPrefix(module, "::"), "::") + pkg := b.cargoOf(sp).Rename(sp.inner(ix.Path), first) + switch { + case pkg == first: + return module + case nested: + return pkg + "::" + rest + } + return pkg +} + // rustSurface adds each pub item of the crate of a unit and each pub // method of its types, under the path that another crate writes for it. func rustSurface(s *surfacer) { diff --git a/projectgraph/testdata/javascript/barrel-chain/graph.golden.json b/projectgraph/testdata/javascript/barrel-chain/graph.golden.json index 43ca85a2..1a713c9f 100644 --- a/projectgraph/testdata/javascript/barrel-chain/graph.golden.json +++ b/projectgraph/testdata/javascript/barrel-chain/graph.golden.json @@ -174,7 +174,7 @@ "src/main.js": "indexed" }, "stats": { - "version": "15", + "version": "16", "files": 4, "nodes": 7, "edges": 6, diff --git a/projectgraph/testdata/javascript/commonjs-require/graph.golden.json b/projectgraph/testdata/javascript/commonjs-require/graph.golden.json index 13192bb9..b8f8c095 100644 --- a/projectgraph/testdata/javascript/commonjs-require/graph.golden.json +++ b/projectgraph/testdata/javascript/commonjs-require/graph.golden.json @@ -138,7 +138,7 @@ "main.js": "indexed" }, "stats": { - "version": "15", + "version": "16", "files": 3, "nodes": 5, "edges": 4, diff --git a/projectgraph/testdata/javascript/default-import/graph.golden.json b/projectgraph/testdata/javascript/default-import/graph.golden.json index dd13406e..bc1b6927 100644 --- a/projectgraph/testdata/javascript/default-import/graph.golden.json +++ b/projectgraph/testdata/javascript/default-import/graph.golden.json @@ -138,7 +138,7 @@ "make.js": "indexed" }, "stats": { - "version": "15", + "version": "16", "files": 3, "nodes": 5, "edges": 4, diff --git a/projectgraph/testdata/javascript/esm-named-import/graph.golden.json b/projectgraph/testdata/javascript/esm-named-import/graph.golden.json index 10ba461d..0595798c 100644 --- a/projectgraph/testdata/javascript/esm-named-import/graph.golden.json +++ b/projectgraph/testdata/javascript/esm-named-import/graph.golden.json @@ -166,7 +166,7 @@ "src/util.js": "indexed" }, "stats": { - "version": "15", + "version": "16", "files": 3, "nodes": 6, "edges": 5, diff --git a/projectgraph/testdata/javascript/export-star/graph.golden.json b/projectgraph/testdata/javascript/export-star/graph.golden.json index 28231c41..cacbbf58 100644 --- a/projectgraph/testdata/javascript/export-star/graph.golden.json +++ b/projectgraph/testdata/javascript/export-star/graph.golden.json @@ -276,7 +276,7 @@ "main.js": "indexed" }, "stats": { - "version": "15", + "version": "16", "files": 5, "nodes": 11, "edges": 10, diff --git a/projectgraph/testdata/javascript/exports-conditions/graph.golden.json b/projectgraph/testdata/javascript/exports-conditions/graph.golden.json index 2ff50fe9..c48789e3 100644 --- a/projectgraph/testdata/javascript/exports-conditions/graph.golden.json +++ b/projectgraph/testdata/javascript/exports-conditions/graph.golden.json @@ -144,7 +144,7 @@ "lib/src/feature/y.js": "indexed" }, "stats": { - "version": "15", + "version": "16", "files": 4, "nodes": 7, "edges": 4, diff --git a/projectgraph/testdata/javascript/unknown-receiver/graph.golden.json b/projectgraph/testdata/javascript/unknown-receiver/graph.golden.json index 130c54fa..5fce3766 100644 --- a/projectgraph/testdata/javascript/unknown-receiver/graph.golden.json +++ b/projectgraph/testdata/javascript/unknown-receiver/graph.golden.json @@ -124,7 +124,7 @@ "main.js": "indexed" }, "stats": { - "version": "15", + "version": "16", "files": 1, "nodes": 5, "edges": 3, diff --git a/projectgraph/testdata/javascript/workspaces/graph.golden.json b/projectgraph/testdata/javascript/workspaces/graph.golden.json index 84461092..7af8a2f2 100644 --- a/projectgraph/testdata/javascript/workspaces/graph.golden.json +++ b/projectgraph/testdata/javascript/workspaces/graph.golden.json @@ -127,7 +127,7 @@ "packages/core/util.js": "indexed" }, "stats": { - "version": "15", + "version": "16", "files": 3, "nodes": 6, "edges": 5, diff --git a/projectgraph/testdata/python/all-hides/graph.golden.json b/projectgraph/testdata/python/all-hides/graph.golden.json index 916b313e..a5519cf5 100644 --- a/projectgraph/testdata/python/all-hides/graph.golden.json +++ b/projectgraph/testdata/python/all-hides/graph.golden.json @@ -174,7 +174,7 @@ "pkg/a.py": "indexed" }, "stats": { - "version": "15", + "version": "16", "files": 3, "nodes": 7, "edges": 6, diff --git a/projectgraph/testdata/python/assignment/graph.golden.json b/projectgraph/testdata/python/assignment/graph.golden.json index 44b94484..50c8c1d7 100644 --- a/projectgraph/testdata/python/assignment/graph.golden.json +++ b/projectgraph/testdata/python/assignment/graph.golden.json @@ -70,7 +70,7 @@ "main.py": "indexed" }, "stats": { - "version": "15", + "version": "16", "files": 1, "nodes": 3, "edges": 2, diff --git a/projectgraph/testdata/python/class-body/graph.golden.json b/projectgraph/testdata/python/class-body/graph.golden.json index 0aa6e4f7..2d7290cb 100644 --- a/projectgraph/testdata/python/class-body/graph.golden.json +++ b/projectgraph/testdata/python/class-body/graph.golden.json @@ -224,7 +224,7 @@ "config.py": "indexed" }, "stats": { - "version": "15", + "version": "16", "files": 1, "nodes": 12, "edges": 7, diff --git a/projectgraph/testdata/python/decorators/graph.golden.json b/projectgraph/testdata/python/decorators/graph.golden.json index 327a0165..69b4630e 100644 --- a/projectgraph/testdata/python/decorators/graph.golden.json +++ b/projectgraph/testdata/python/decorators/graph.golden.json @@ -194,7 +194,7 @@ "app.py": "indexed" }, "stats": { - "version": "15", + "version": "16", "files": 1, "nodes": 9, "edges": 5, diff --git a/projectgraph/testdata/python/external/graph.golden.json b/projectgraph/testdata/python/external/graph.golden.json index 070797df..8e6116a8 100644 --- a/projectgraph/testdata/python/external/graph.golden.json +++ b/projectgraph/testdata/python/external/graph.golden.json @@ -108,7 +108,7 @@ "main.py": "indexed" }, "stats": { - "version": "15", + "version": "16", "files": 1, "nodes": 5, "edges": 3, diff --git a/projectgraph/testdata/python/local-receiver/graph.golden.json b/projectgraph/testdata/python/local-receiver/graph.golden.json index e8f105cb..3a333db8 100644 --- a/projectgraph/testdata/python/local-receiver/graph.golden.json +++ b/projectgraph/testdata/python/local-receiver/graph.golden.json @@ -145,7 +145,7 @@ "main.py": "indexed" }, "stats": { - "version": "15", + "version": "16", "files": 1, "nodes": 5, "edges": 3, diff --git a/projectgraph/testdata/python/member-call/graph.golden.json b/projectgraph/testdata/python/member-call/graph.golden.json index 36c7475e..f2d92a37 100644 --- a/projectgraph/testdata/python/member-call/graph.golden.json +++ b/projectgraph/testdata/python/member-call/graph.golden.json @@ -258,7 +258,7 @@ "parser.py": "indexed" }, "stats": { - "version": "15", + "version": "16", "files": 3, "nodes": 9, "edges": 10, diff --git a/projectgraph/testdata/python/missing-file/graph.golden.json b/projectgraph/testdata/python/missing-file/graph.golden.json index f57c6daa..249c0096 100644 --- a/projectgraph/testdata/python/missing-file/graph.golden.json +++ b/projectgraph/testdata/python/missing-file/graph.golden.json @@ -55,7 +55,7 @@ "main.py": "cached" }, "stats": { - "version": "15", + "version": "16", "files": 2, "nodes": 2, "edges": 2, diff --git a/projectgraph/testdata/python/package-init/graph.golden.json b/projectgraph/testdata/python/package-init/graph.golden.json index 0f605f44..3ae14b1f 100644 --- a/projectgraph/testdata/python/package-init/graph.golden.json +++ b/projectgraph/testdata/python/package-init/graph.golden.json @@ -144,7 +144,7 @@ "pkg/loader.py": "indexed" }, "stats": { - "version": "15", + "version": "16", "files": 3, "nodes": 6, "edges": 5, diff --git a/projectgraph/testdata/python/reexport-cycle/graph.golden.json b/projectgraph/testdata/python/reexport-cycle/graph.golden.json index b4af31d2..c9b069c0 100644 --- a/projectgraph/testdata/python/reexport-cycle/graph.golden.json +++ b/projectgraph/testdata/python/reexport-cycle/graph.golden.json @@ -105,7 +105,7 @@ "main.py": "indexed" }, "stats": { - "version": "15", + "version": "16", "files": 3, "nodes": 3, "edges": 3, diff --git a/projectgraph/testdata/python/relative-imports/graph.golden.json b/projectgraph/testdata/python/relative-imports/graph.golden.json index 09b99a12..fd51479a 100644 --- a/projectgraph/testdata/python/relative-imports/graph.golden.json +++ b/projectgraph/testdata/python/relative-imports/graph.golden.json @@ -154,7 +154,7 @@ "pkg/sub/b.py": "indexed" }, "stats": { - "version": "15", + "version": "16", "files": 3, "nodes": 6, "edges": 4, diff --git a/projectgraph/testdata/python/sibling-import/graph.golden.json b/projectgraph/testdata/python/sibling-import/graph.golden.json index b2031df1..cebc8e7d 100644 --- a/projectgraph/testdata/python/sibling-import/graph.golden.json +++ b/projectgraph/testdata/python/sibling-import/graph.golden.json @@ -98,7 +98,7 @@ "main.py": "indexed" }, "stats": { - "version": "15", + "version": "16", "files": 2, "nodes": 4, "edges": 3, diff --git a/projectgraph/testdata/python/src-layout/graph.golden.json b/projectgraph/testdata/python/src-layout/graph.golden.json index 659fba14..9f2d1eb9 100644 --- a/projectgraph/testdata/python/src-layout/graph.golden.json +++ b/projectgraph/testdata/python/src-layout/graph.golden.json @@ -99,7 +99,7 @@ "services/api/src/api/core.py": "indexed" }, "stats": { - "version": "15", + "version": "16", "files": 3, "nodes": 4, "edges": 2, diff --git a/projectgraph/testdata/python/sys-path/graph.golden.json b/projectgraph/testdata/python/sys-path/graph.golden.json index b7f23a3b..2ed50c1b 100644 --- a/projectgraph/testdata/python/sys-path/graph.golden.json +++ b/projectgraph/testdata/python/sys-path/graph.golden.json @@ -82,7 +82,7 @@ "lib/shared.py": "indexed" }, "stats": { - "version": "15", + "version": "16", "files": 2, "nodes": 3, "edges": 2, diff --git a/projectgraph/testdata/python/wildcard/graph.golden.json b/projectgraph/testdata/python/wildcard/graph.golden.json index 03ea7941..db310758 100644 --- a/projectgraph/testdata/python/wildcard/graph.golden.json +++ b/projectgraph/testdata/python/wildcard/graph.golden.json @@ -112,7 +112,7 @@ "util.py": "indexed" }, "stats": { - "version": "15", + "version": "16", "files": 2, "nodes": 4, "edges": 3, diff --git a/projectgraph/testdata/roots/extra-rules/graph.golden.json b/projectgraph/testdata/roots/extra-rules/graph.golden.json index 4ff46249..da675a69 100644 --- a/projectgraph/testdata/roots/extra-rules/graph.golden.json +++ b/projectgraph/testdata/roots/extra-rules/graph.golden.json @@ -105,7 +105,7 @@ "views.py": "indexed" }, "stats": { - "version": "15", + "version": "16", "files": 3, "nodes": 7, "edges": 0, diff --git a/projectgraph/testdata/roots/library-surface/graph.golden.json b/projectgraph/testdata/roots/library-surface/graph.golden.json index f2fc364e..5c8b68da 100644 --- a/projectgraph/testdata/roots/library-surface/graph.golden.json +++ b/projectgraph/testdata/roots/library-surface/graph.golden.json @@ -82,7 +82,7 @@ "lib/test_api.py": "indexed" }, "stats": { - "version": "15", + "version": "16", "files": 2, "nodes": 5, "edges": 0, diff --git a/projectgraph/testdata/roots/main-guard/graph.golden.json b/projectgraph/testdata/roots/main-guard/graph.golden.json index 1f84fa12..d5b4b552 100644 --- a/projectgraph/testdata/roots/main-guard/graph.golden.json +++ b/projectgraph/testdata/roots/main-guard/graph.golden.json @@ -60,7 +60,7 @@ "lib.py": "indexed" }, "stats": { - "version": "15", + "version": "16", "files": 2, "nodes": 4, "edges": 1, diff --git a/projectgraph/testdata/roots/package-json-bin/graph.golden.json b/projectgraph/testdata/roots/package-json-bin/graph.golden.json index 0767050c..11a8e36f 100644 --- a/projectgraph/testdata/roots/package-json-bin/graph.golden.json +++ b/projectgraph/testdata/roots/package-json-bin/graph.golden.json @@ -64,7 +64,7 @@ "lib/run.js": "indexed" }, "stats": { - "version": "15", + "version": "16", "files": 2, "nodes": 3, "edges": 2, diff --git a/projectgraph/testdata/roots/package-json-main/graph.golden.json b/projectgraph/testdata/roots/package-json-main/graph.golden.json index ae5ae680..dc63358f 100644 --- a/projectgraph/testdata/roots/package-json-main/graph.golden.json +++ b/projectgraph/testdata/roots/package-json-main/graph.golden.json @@ -154,7 +154,7 @@ "parser.js": "indexed" }, "stats": { - "version": "15", + "version": "16", "files": 4, "nodes": 9, "edges": 3, diff --git a/projectgraph/testdata/roots/procfile-dockerfile/graph.golden.json b/projectgraph/testdata/roots/procfile-dockerfile/graph.golden.json index 3b6d8839..4b86f86c 100644 --- a/projectgraph/testdata/roots/procfile-dockerfile/graph.golden.json +++ b/projectgraph/testdata/roots/procfile-dockerfile/graph.golden.json @@ -108,7 +108,7 @@ "worker.py": "indexed" }, "stats": { - "version": "15", + "version": "16", "files": 6, "nodes": 7, "edges": 0, diff --git a/projectgraph/testdata/roots/pyproject-scripts/graph.golden.json b/projectgraph/testdata/roots/pyproject-scripts/graph.golden.json index 5f12c9d0..62d83799 100644 --- a/projectgraph/testdata/roots/pyproject-scripts/graph.golden.json +++ b/projectgraph/testdata/roots/pyproject-scripts/graph.golden.json @@ -146,7 +146,7 @@ "src/app/tool.py": "indexed" }, "stats": { - "version": "15", + "version": "16", "files": 5, "nodes": 10, "edges": 0, diff --git a/projectgraph/testdata/roots/setup-py/graph.golden.json b/projectgraph/testdata/roots/setup-py/graph.golden.json index e812cfe7..ab3a569a 100644 --- a/projectgraph/testdata/roots/setup-py/graph.golden.json +++ b/projectgraph/testdata/roots/setup-py/graph.golden.json @@ -74,7 +74,7 @@ "tool/main.py": "indexed" }, "stats": { - "version": "15", + "version": "16", "files": 2, "nodes": 4, "edges": 0, diff --git a/projectgraph/testdata/roots/test-file/graph.golden.json b/projectgraph/testdata/roots/test-file/graph.golden.json index 183bfd7c..97089938 100644 --- a/projectgraph/testdata/roots/test-file/graph.golden.json +++ b/projectgraph/testdata/roots/test-file/graph.golden.json @@ -68,7 +68,7 @@ "tests/test_app.py": "indexed" }, "stats": { - "version": "15", + "version": "16", "files": 1, "nodes": 4, "edges": 0, diff --git a/projectgraph/testdata/typescript/js-specifier/graph.golden.json b/projectgraph/testdata/typescript/js-specifier/graph.golden.json index 138c4d69..b1ffb28d 100644 --- a/projectgraph/testdata/typescript/js-specifier/graph.golden.json +++ b/projectgraph/testdata/typescript/js-specifier/graph.golden.json @@ -82,7 +82,7 @@ "src/util.ts": "indexed" }, "stats": { - "version": "15", + "version": "16", "files": 2, "nodes": 3, "edges": 2, diff --git a/projectgraph/testdata/typescript/tsconfig-paths/graph.golden.json b/projectgraph/testdata/typescript/tsconfig-paths/graph.golden.json index 09668d30..81b607c7 100644 --- a/projectgraph/testdata/typescript/tsconfig-paths/graph.golden.json +++ b/projectgraph/testdata/typescript/tsconfig-paths/graph.golden.json @@ -138,7 +138,7 @@ "src/main.ts": "indexed" }, "stats": { - "version": "15", + "version": "16", "files": 3, "nodes": 5, "edges": 4, diff --git a/projectgraph/testdata/typescript/type-only/graph.golden.json b/projectgraph/testdata/typescript/type-only/graph.golden.json index 96859381..327d10a3 100644 --- a/projectgraph/testdata/typescript/type-only/graph.golden.json +++ b/projectgraph/testdata/typescript/type-only/graph.golden.json @@ -97,7 +97,7 @@ "src/types.ts": "indexed" }, "stats": { - "version": "15", + "version": "16", "files": 3, "nodes": 5, "edges": 2, diff --git a/test/projectgraph/cases/go/dependency-build-files/summaries/go_github.com_acme_kit_v1.0.0.json b/test/projectgraph/cases/go/dependency-build-files/summaries/go_github.com_acme_kit_v1.0.0.json index ce73005d..d75c8c99 100644 --- a/test/projectgraph/cases/go/dependency-build-files/summaries/go_github.com_acme_kit_v1.0.0.json +++ b/test/projectgraph/cases/go/dependency-build-files/summaries/go_github.com_acme_kit_v1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "14", - "link_version": "15", + "link_version": "16", "unit": "go:github.com/acme/kit@v1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/go/dependency-generated-code/summaries/go_github.com_acme_kit_v1.0.0.json b/test/projectgraph/cases/go/dependency-generated-code/summaries/go_github.com_acme_kit_v1.0.0.json index bbd4b747..bd84fe61 100644 --- a/test/projectgraph/cases/go/dependency-generated-code/summaries/go_github.com_acme_kit_v1.0.0.json +++ b/test/projectgraph/cases/go/dependency-generated-code/summaries/go_github.com_acme_kit_v1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "14", - "link_version": "15", + "link_version": "16", "unit": "go:github.com/acme/kit@v1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/go/dependency-implements-interface/summaries/go_github.com_acme_dep_v1.0.0.json b/test/projectgraph/cases/go/dependency-implements-interface/summaries/go_github.com_acme_dep_v1.0.0.json index 3e1ed233..69acb2cf 100644 --- a/test/projectgraph/cases/go/dependency-implements-interface/summaries/go_github.com_acme_dep_v1.0.0.json +++ b/test/projectgraph/cases/go/dependency-implements-interface/summaries/go_github.com_acme_dep_v1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "14", - "link_version": "15", + "link_version": "16", "unit": "go:github.com/acme/dep@v1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/go/dependency-internal-call/summaries/go_github.com_acme_kit_v1.0.0.json b/test/projectgraph/cases/go/dependency-internal-call/summaries/go_github.com_acme_kit_v1.0.0.json index 02b64946..e8235fda 100644 --- a/test/projectgraph/cases/go/dependency-internal-call/summaries/go_github.com_acme_kit_v1.0.0.json +++ b/test/projectgraph/cases/go/dependency-internal-call/summaries/go_github.com_acme_kit_v1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "14", - "link_version": "15", + "link_version": "16", "unit": "go:github.com/acme/kit@v1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/go/dependency-module/summaries/go_github.com_acme_yamlish_v1.2.0.json b/test/projectgraph/cases/go/dependency-module/summaries/go_github.com_acme_yamlish_v1.2.0.json index c37f2c5c..c27b2ee8 100644 --- a/test/projectgraph/cases/go/dependency-module/summaries/go_github.com_acme_yamlish_v1.2.0.json +++ b/test/projectgraph/cases/go/dependency-module/summaries/go_github.com_acme_yamlish_v1.2.0.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "14", - "link_version": "15", + "link_version": "16", "unit": "go:github.com/acme/yamlish@v1.2.0", "symbols": [ { diff --git a/test/projectgraph/cases/go/dependency-platform-files/summaries/go_github.com_acme_kit_v1.0.0.json b/test/projectgraph/cases/go/dependency-platform-files/summaries/go_github.com_acme_kit_v1.0.0.json index bbd4b747..bd84fe61 100644 --- a/test/projectgraph/cases/go/dependency-platform-files/summaries/go_github.com_acme_kit_v1.0.0.json +++ b/test/projectgraph/cases/go/dependency-platform-files/summaries/go_github.com_acme_kit_v1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "14", - "link_version": "15", + "link_version": "16", "unit": "go:github.com/acme/kit@v1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/go/dependency-platform-results/expect.yaml b/test/projectgraph/cases/go/dependency-platform-results/expect.yaml new file mode 100644 index 00000000..7efe2e58 --- /dev/null +++ b/test/projectgraph/cases/go/dependency-platform-results/expect.yaml @@ -0,0 +1,37 @@ +source: safedep:hand +description: >- + new_linux.go and new_windows.go each define New, and they return the types Linux and Windows. Both types have + the method Common. The walk follows one result type, so the call New().Common() is a record with the reason + unknown-receiver. Before, the walk took the type of the first file, and Windows//Common was not-found. +units: + gomod/github.com/acme/kit@v1.0.0: go:github.com/acme/kit@v1.0.0 +roots: + - key: main.go + kind: main + rule: go.main + - key: main.go//main + kind: main + rule: go.main +unresolved: + - caller: github.com//acme//kit//New//Common + site: go:github.com/acme/kit@v1.0.0 + reason: unknown-receiver + - caller: main.go//main + site: main.go:6 + reason: unknown-receiver + method: Common +results: + - target: github.com//acme//kit//Windows//Common + status: uncertain + uncertainties: + - kind: unresolved-call + site: main.go:6 + - kind: unresolved-call + site: go:github.com/acme/kit@v1.0.0 + - target: github.com//acme//kit//Linux//Common + status: uncertain + uncertainties: + - kind: unresolved-call + site: main.go:6 + - kind: unresolved-call + site: go:github.com/acme/kit@v1.0.0 diff --git a/test/projectgraph/cases/go/dependency-platform-results/project/go.mod b/test/projectgraph/cases/go/dependency-platform-results/project/go.mod new file mode 100644 index 00000000..c7090c9c --- /dev/null +++ b/test/projectgraph/cases/go/dependency-platform-results/project/go.mod @@ -0,0 +1,5 @@ +module app + +go 1.22 + +require github.com/acme/kit v1.0.0 diff --git a/test/projectgraph/cases/go/dependency-platform-results/project/gomod/github.com/acme/kit@v1.0.0/go.mod b/test/projectgraph/cases/go/dependency-platform-results/project/gomod/github.com/acme/kit@v1.0.0/go.mod new file mode 100644 index 00000000..529dd612 --- /dev/null +++ b/test/projectgraph/cases/go/dependency-platform-results/project/gomod/github.com/acme/kit@v1.0.0/go.mod @@ -0,0 +1,3 @@ +module github.com/acme/kit + +go 1.22 diff --git a/test/projectgraph/cases/go/dependency-platform-results/project/gomod/github.com/acme/kit@v1.0.0/new_linux.go b/test/projectgraph/cases/go/dependency-platform-results/project/gomod/github.com/acme/kit@v1.0.0/new_linux.go new file mode 100644 index 00000000..27f8ead1 --- /dev/null +++ b/test/projectgraph/cases/go/dependency-platform-results/project/gomod/github.com/acme/kit@v1.0.0/new_linux.go @@ -0,0 +1,7 @@ +package kit + +type Linux struct{} + +func (*Linux) Common() {} + +func New() *Linux { return &Linux{} } diff --git a/test/projectgraph/cases/go/dependency-platform-results/project/gomod/github.com/acme/kit@v1.0.0/new_windows.go b/test/projectgraph/cases/go/dependency-platform-results/project/gomod/github.com/acme/kit@v1.0.0/new_windows.go new file mode 100644 index 00000000..7268f125 --- /dev/null +++ b/test/projectgraph/cases/go/dependency-platform-results/project/gomod/github.com/acme/kit@v1.0.0/new_windows.go @@ -0,0 +1,9 @@ +package kit + +type Windows struct{} + +func (*Windows) Common() {} + +func (*Windows) WindowsOnly() {} + +func New() *Windows { return &Windows{} } diff --git a/test/projectgraph/cases/go/dependency-platform-results/project/main.go b/test/projectgraph/cases/go/dependency-platform-results/project/main.go new file mode 100644 index 00000000..5d9844d5 --- /dev/null +++ b/test/projectgraph/cases/go/dependency-platform-results/project/main.go @@ -0,0 +1,7 @@ +package main + +import "github.com/acme/kit" + +func main() { + kit.New().Common() +} diff --git a/test/projectgraph/cases/go/dependency-platform-results/summaries/go_github.com_acme_kit_v1.0.0.json b/test/projectgraph/cases/go/dependency-platform-results/summaries/go_github.com_acme_kit_v1.0.0.json new file mode 100644 index 00000000..bc6a5746 --- /dev/null +++ b/test/projectgraph/cases/go/dependency-platform-results/summaries/go_github.com_acme_kit_v1.0.0.json @@ -0,0 +1,51 @@ +{ + "version": "2", + "index_version": "14", + "link_version": "16", + "unit": "go:github.com/acme/kit@v1.0.0", + "symbols": [ + { + "key": "github.com//acme//kit//Linux", + "definitions": [ + "go:github.com/acme/kit@v1.0.0//new_linux.go//Linux" + ], + "reaches": [] + }, + { + "key": "github.com//acme//kit//Linux//Common", + "definitions": [ + "go:github.com/acme/kit@v1.0.0//new_linux.go//Linux//Common" + ], + "reaches": [] + }, + { + "key": "github.com//acme//kit//New", + "definitions": [ + "go:github.com/acme/kit@v1.0.0//new_linux.go//New" + ], + "reaches": [] + }, + { + "key": "github.com//acme//kit//Windows", + "definitions": [ + "go:github.com/acme/kit@v1.0.0//new_windows.go//Windows" + ], + "reaches": [] + }, + { + "key": "github.com//acme//kit//Windows//Common", + "definitions": [ + "go:github.com/acme/kit@v1.0.0//new_windows.go//Windows//Common" + ], + "reaches": [] + }, + { + "key": "github.com//acme//kit//Windows//WindowsOnly", + "definitions": [ + "go:github.com/acme/kit@v1.0.0//new_windows.go//Windows//WindowsOnly" + ], + "reaches": [] + } + ], + "targets": [] +} diff --git a/test/projectgraph/cases/go/dependency-result-types/summaries/go_example.com_markup_v1.0.0.json b/test/projectgraph/cases/go/dependency-result-types/summaries/go_example.com_markup_v1.0.0.json index 8fb94de6..3ecc19d6 100644 --- a/test/projectgraph/cases/go/dependency-result-types/summaries/go_example.com_markup_v1.0.0.json +++ b/test/projectgraph/cases/go/dependency-result-types/summaries/go_example.com_markup_v1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "14", - "link_version": "15", + "link_version": "16", "unit": "go:example.com/markup@v1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/go/dependency-result-types/summaries/go_std_go1.25.1.json b/test/projectgraph/cases/go/dependency-result-types/summaries/go_std_go1.25.1.json index ed174cb8..a138a779 100644 --- a/test/projectgraph/cases/go/dependency-result-types/summaries/go_std_go1.25.1.json +++ b/test/projectgraph/cases/go/dependency-result-types/summaries/go_std_go1.25.1.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "14", - "link_version": "15", + "link_version": "16", "unit": "go:std@go1.25.1", "symbols": [ { diff --git a/test/projectgraph/cases/javascript/dependency-candidates/summaries/npm_store-kit_1.0.0.json b/test/projectgraph/cases/javascript/dependency-candidates/summaries/npm_store-kit_1.0.0.json index df3b02f5..5e45d1ab 100644 --- a/test/projectgraph/cases/javascript/dependency-candidates/summaries/npm_store-kit_1.0.0.json +++ b/test/projectgraph/cases/javascript/dependency-candidates/summaries/npm_store-kit_1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "14", - "link_version": "15", + "link_version": "16", "unit": "npm:store-kit@1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/javascript/dependency-exports-import/summaries/npm_kit_1.0.0.json b/test/projectgraph/cases/javascript/dependency-exports-import/summaries/npm_kit_1.0.0.json index 22e1431e..c094a670 100644 --- a/test/projectgraph/cases/javascript/dependency-exports-import/summaries/npm_kit_1.0.0.json +++ b/test/projectgraph/cases/javascript/dependency-exports-import/summaries/npm_kit_1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "14", - "link_version": "15", + "link_version": "16", "unit": "npm:kit@1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/javascript/dependency-exports-require/summaries/npm_kit_1.0.0.json b/test/projectgraph/cases/javascript/dependency-exports-require/summaries/npm_kit_1.0.0.json index 22e1431e..c094a670 100644 --- a/test/projectgraph/cases/javascript/dependency-exports-require/summaries/npm_kit_1.0.0.json +++ b/test/projectgraph/cases/javascript/dependency-exports-require/summaries/npm_kit_1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "14", - "link_version": "15", + "link_version": "16", "unit": "npm:kit@1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/javascript/dependency-method-name/summaries/npm_plug_1.0.0.json b/test/projectgraph/cases/javascript/dependency-method-name/summaries/npm_plug_1.0.0.json index e7f84498..3840a85b 100644 --- a/test/projectgraph/cases/javascript/dependency-method-name/summaries/npm_plug_1.0.0.json +++ b/test/projectgraph/cases/javascript/dependency-method-name/summaries/npm_plug_1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "14", - "link_version": "15", + "link_version": "16", "unit": "npm:plug@1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/javascript/dependency-same-path/summaries/npm_util-kit_1.0.0.json b/test/projectgraph/cases/javascript/dependency-same-path/summaries/npm_util-kit_1.0.0.json index dfede16a..0ee53d7c 100644 --- a/test/projectgraph/cases/javascript/dependency-same-path/summaries/npm_util-kit_1.0.0.json +++ b/test/projectgraph/cases/javascript/dependency-same-path/summaries/npm_util-kit_1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "14", - "link_version": "15", + "link_version": "16", "unit": "npm:util-kit@1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/javascript/dependency-transitive/summaries/npm_lodash._reinterpolate_3.0.0.json b/test/projectgraph/cases/javascript/dependency-transitive/summaries/npm_lodash._reinterpolate_3.0.0.json index cf89d8cf..7d16d7d1 100644 --- a/test/projectgraph/cases/javascript/dependency-transitive/summaries/npm_lodash._reinterpolate_3.0.0.json +++ b/test/projectgraph/cases/javascript/dependency-transitive/summaries/npm_lodash._reinterpolate_3.0.0.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "14", - "link_version": "15", + "link_version": "16", "unit": "npm:lodash._reinterpolate@3.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/javascript/dependency-transitive/summaries/npm_lodash_4.17.21.json b/test/projectgraph/cases/javascript/dependency-transitive/summaries/npm_lodash_4.17.21.json index 48a600cf..e177f536 100644 --- a/test/projectgraph/cases/javascript/dependency-transitive/summaries/npm_lodash_4.17.21.json +++ b/test/projectgraph/cases/javascript/dependency-transitive/summaries/npm_lodash_4.17.21.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "14", - "link_version": "15", + "link_version": "16", "unit": "npm:lodash@4.17.21", "symbols": [ { diff --git a/test/projectgraph/cases/python/dependency-dark-unit/summaries/pypi_PyYAML_6.0.1.json b/test/projectgraph/cases/python/dependency-dark-unit/summaries/pypi_PyYAML_6.0.1.json index c491bc6a..5457ea6f 100644 --- a/test/projectgraph/cases/python/dependency-dark-unit/summaries/pypi_PyYAML_6.0.1.json +++ b/test/projectgraph/cases/python/dependency-dark-unit/summaries/pypi_PyYAML_6.0.1.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "14", - "link_version": "15", + "link_version": "16", "unit": "pypi:PyYAML@6.0.1", "symbols": [ { diff --git a/test/projectgraph/cases/python/dependency-failed-reached/summaries/pypi_badpkg_1.0.0.json b/test/projectgraph/cases/python/dependency-failed-reached/summaries/pypi_badpkg_1.0.0.json index 9d6cece7..fb669126 100644 --- a/test/projectgraph/cases/python/dependency-failed-reached/summaries/pypi_badpkg_1.0.0.json +++ b/test/projectgraph/cases/python/dependency-failed-reached/summaries/pypi_badpkg_1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "14", - "link_version": "15", + "link_version": "16", "unit": "pypi:badpkg@1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/python/dependency-failed-reached/summaries/pypi_okpkg_1.0.0.json b/test/projectgraph/cases/python/dependency-failed-reached/summaries/pypi_okpkg_1.0.0.json index 62618129..39710708 100644 --- a/test/projectgraph/cases/python/dependency-failed-reached/summaries/pypi_okpkg_1.0.0.json +++ b/test/projectgraph/cases/python/dependency-failed-reached/summaries/pypi_okpkg_1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "14", - "link_version": "15", + "link_version": "16", "unit": "pypi:okpkg@1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/python/dependency-failed-unreached/summaries/pypi_badpkg_1.0.0.json b/test/projectgraph/cases/python/dependency-failed-unreached/summaries/pypi_badpkg_1.0.0.json index 9d6cece7..fb669126 100644 --- a/test/projectgraph/cases/python/dependency-failed-unreached/summaries/pypi_badpkg_1.0.0.json +++ b/test/projectgraph/cases/python/dependency-failed-unreached/summaries/pypi_badpkg_1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "14", - "link_version": "15", + "link_version": "16", "unit": "pypi:badpkg@1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/python/dependency-failed-unreached/summaries/pypi_okpkg_1.0.0.json b/test/projectgraph/cases/python/dependency-failed-unreached/summaries/pypi_okpkg_1.0.0.json index 62618129..39710708 100644 --- a/test/projectgraph/cases/python/dependency-failed-unreached/summaries/pypi_okpkg_1.0.0.json +++ b/test/projectgraph/cases/python/dependency-failed-unreached/summaries/pypi_okpkg_1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "14", - "link_version": "15", + "link_version": "16", "unit": "pypi:okpkg@1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/python/dependency-package-dir/summaries/pypi_outer_1.0.0.json b/test/projectgraph/cases/python/dependency-package-dir/summaries/pypi_outer_1.0.0.json index 6f891293..060315ed 100644 --- a/test/projectgraph/cases/python/dependency-package-dir/summaries/pypi_outer_1.0.0.json +++ b/test/projectgraph/cases/python/dependency-package-dir/summaries/pypi_outer_1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "14", - "link_version": "15", + "link_version": "16", "unit": "pypi:outer@1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/python/dependency-site-packages/summaries/pypi_requests_2.31.0.json b/test/projectgraph/cases/python/dependency-site-packages/summaries/pypi_requests_2.31.0.json index ecccd43b..064e485d 100644 --- a/test/projectgraph/cases/python/dependency-site-packages/summaries/pypi_requests_2.31.0.json +++ b/test/projectgraph/cases/python/dependency-site-packages/summaries/pypi_requests_2.31.0.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "14", - "link_version": "15", + "link_version": "16", "unit": "pypi:requests@2.31.0", "symbols": [ { diff --git a/test/projectgraph/cases/python/dependency-site-packages/summaries/pypi_urllib3_2.0.7.json b/test/projectgraph/cases/python/dependency-site-packages/summaries/pypi_urllib3_2.0.7.json index 47a3bc13..567fa307 100644 --- a/test/projectgraph/cases/python/dependency-site-packages/summaries/pypi_urllib3_2.0.7.json +++ b/test/projectgraph/cases/python/dependency-site-packages/summaries/pypi_urllib3_2.0.7.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "14", - "link_version": "15", + "link_version": "16", "unit": "pypi:urllib3@2.0.7", "symbols": [ { diff --git a/test/projectgraph/cases/rust/dependency-crate/summaries/cargo_kit_1.0.0.json b/test/projectgraph/cases/rust/dependency-crate/summaries/cargo_kit_1.0.0.json index 3b7060db..4bc0b860 100644 --- a/test/projectgraph/cases/rust/dependency-crate/summaries/cargo_kit_1.0.0.json +++ b/test/projectgraph/cases/rust/dependency-crate/summaries/cargo_kit_1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "14", - "link_version": "15", + "link_version": "16", "unit": "cargo:kit@1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/rust/dependency-internal-call/summaries/cargo_kit_1.0.0.json b/test/projectgraph/cases/rust/dependency-internal-call/summaries/cargo_kit_1.0.0.json index 56a598a7..d7c8d300 100644 --- a/test/projectgraph/cases/rust/dependency-internal-call/summaries/cargo_kit_1.0.0.json +++ b/test/projectgraph/cases/rust/dependency-internal-call/summaries/cargo_kit_1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "14", - "link_version": "15", + "link_version": "16", "unit": "cargo:kit@1.0.0", "symbols": [ { diff --git a/test/projectgraph/cases/rust/dependency-renamed/expect.yaml b/test/projectgraph/cases/rust/dependency-renamed/expect.yaml new file mode 100644 index 00000000..fae15315 --- /dev/null +++ b/test/projectgraph/cases/rust/dependency-renamed/expect.yaml @@ -0,0 +1,36 @@ +source: safedep:hand +description: >- + The Cargo.toml of the project renames the package kit to helper, and main calls helper::serve. The link step + reads the rename, so the import names the crate kit of the unit cargo:kit@1.0.0, and reach finds the private + parse_header through serve. Before the link step read renames, the target was not-found. +units: + vendor/kit: cargo:kit@1.0.0 +roots: + - key: src/main.rs + kind: main + rule: rust.main + - key: src/main.rs//main + kind: main + rule: rust.main +unresolved: [] +results: + - target: kit//parse_header + status: found + path: + - key: src/main.rs//main + - key: helper//serve + site: src/main.rs:2 + kind: direct + confidence: high + - key: cargo:kit@1.0.0//src/lib.rs//serve + site: cargo:kit@1.0.0//src/lib.rs:1 + kind: package + confidence: high + - key: cargo:kit@1.0.0//src/lib.rs//parse_header + site: cargo:kit@1.0.0//src/lib.rs:2 + kind: direct + confidence: high + call_sites: + - caller: cargo:kit@1.0.0//src/lib.rs//serve + site: cargo:kit@1.0.0//src/lib.rs:2 + reached: true diff --git a/test/projectgraph/cases/rust/dependency-renamed/project/Cargo.toml b/test/projectgraph/cases/rust/dependency-renamed/project/Cargo.toml new file mode 100644 index 00000000..1e4dd14a --- /dev/null +++ b/test/projectgraph/cases/rust/dependency-renamed/project/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "app" +version = "0.1.0" +edition = "2021" + +[dependencies] +helper = { package = "kit", version = "1.0.0" } diff --git a/test/projectgraph/cases/rust/dependency-renamed/project/src/main.rs b/test/projectgraph/cases/rust/dependency-renamed/project/src/main.rs new file mode 100644 index 00000000..42454ac3 --- /dev/null +++ b/test/projectgraph/cases/rust/dependency-renamed/project/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + helper::serve(); +} diff --git a/test/projectgraph/cases/rust/dependency-renamed/project/vendor/kit/Cargo.toml b/test/projectgraph/cases/rust/dependency-renamed/project/vendor/kit/Cargo.toml new file mode 100644 index 00000000..58da5f79 --- /dev/null +++ b/test/projectgraph/cases/rust/dependency-renamed/project/vendor/kit/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "kit" +version = "1.0.0" +edition = "2021" diff --git a/test/projectgraph/cases/rust/dependency-renamed/project/vendor/kit/src/lib.rs b/test/projectgraph/cases/rust/dependency-renamed/project/vendor/kit/src/lib.rs new file mode 100644 index 00000000..7746db0b --- /dev/null +++ b/test/projectgraph/cases/rust/dependency-renamed/project/vendor/kit/src/lib.rs @@ -0,0 +1,5 @@ +pub fn serve() { + parse_header(); +} + +fn parse_header() {} diff --git a/test/projectgraph/cases/rust/dependency-renamed/summaries/cargo_kit_1.0.0.json b/test/projectgraph/cases/rust/dependency-renamed/summaries/cargo_kit_1.0.0.json new file mode 100644 index 00000000..d7c8d300 --- /dev/null +++ b/test/projectgraph/cases/rust/dependency-renamed/summaries/cargo_kit_1.0.0.json @@ -0,0 +1,16 @@ +{ + "version": "2", + "index_version": "14", + "link_version": "16", + "unit": "cargo:kit@1.0.0", + "symbols": [ + { + "key": "kit//serve", + "definitions": [ + "cargo:kit@1.0.0//src/lib.rs//serve" + ], + "reaches": [] + } + ], + "targets": [] +} diff --git a/test/projectgraph/cases/rust/library-build-script/summaries/cargo_kit_1.0.0.json b/test/projectgraph/cases/rust/library-build-script/summaries/cargo_kit_1.0.0.json index 90a2c656..aa9775d4 100644 --- a/test/projectgraph/cases/rust/library-build-script/summaries/cargo_kit_1.0.0.json +++ b/test/projectgraph/cases/rust/library-build-script/summaries/cargo_kit_1.0.0.json @@ -1,7 +1,7 @@ { "version": "2", "index_version": "14", - "link_version": "15", + "link_version": "16", "unit": "cargo:kit@1.0.0", "symbols": [ { diff --git a/test/projectgraph/version_test.go b/test/projectgraph/version_test.go index 10054342..4fd1d2e5 100644 --- a/test/projectgraph/version_test.go +++ b/test/projectgraph/version_test.go @@ -30,6 +30,7 @@ var graphHashes = map[string]string{ "13/13": "9383a0bc5c6e8b760beb443112cae1400c39212c1da5c2d155c22488eb00eaa6", "14/14": "0f3c9c38b39f4de1835f34e8a58bcea85461f37e7aa2d5a2dd484569e670ff48", "14/15": "bbc708aba767ccf554c75a7e06816abda3fc1804ad6d0876ad8ce9575f4c3a88", + "14/16": "823b1f82c4aed8ba27d19275505c8d1dee755086be48fcda64d323f55b4c62a5", } // resultHashes records the hash of the reach results of every case for @@ -44,6 +45,7 @@ var resultHashes = map[string]string{ "13/13/2": "2f81b00e1708cbb7e0589f34eaa7866a5658a6cfc0a7608d532381ca4267ba06", "14/14/2": "a832ea709bf3df852814375a56e6329c35e2ff4c49ae34ee49afe042e9502d17", "14/15/3": "77dfd0ea8ab71869ef612d41b8c2e46852d4fb67a49458cb645321692af77ea0", + "14/16/3": "13b6eecb686ddec3c2ba470fc9752ea66cff445d5e18e0772a71b15ae7fe1c9c", } // summaryHashes records the hash of the golden summaries of the cases for @@ -59,6 +61,7 @@ var summaryHashes = map[string]string{ "13/13/2": "dc844b1cd13e7873f54d646f27384c78b64707e13ae9c9ef27a4557ba7ba4dfc", "14/14/2": "27424c4e2525450479faba78421959738748811d70b9a93b81e2bbdf1733bf0d", "14/15/2": "5969f3cda15b666cbd902bbc2db953cbdef8c0ce48d5e77446cf243cdd0bb0e7", + "14/16/2": "fb678202978212aae932e50a56a0d3dc7f0f794ce908988bf605ce9a6e04bc51", } func TestGraphHashFollowsVersion(t *testing.T) { From 5f6cd9fdd656da692135f05a1b192dd34d5cbfef Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 19 Oct 2026 10:17:50 +0000 Subject: [PATCH 9/9] docs(report): record the measure after the review fixes of PR 53 Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01PFQbqPqd9xn1fT6hBsyoUm Signed-off-by: Claude --- test/projectgraph/report.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/projectgraph/report.md b/test/projectgraph/report.md index 805e59f1..d2c602a6 100644 --- a/test/projectgraph/report.md +++ b/test/projectgraph/report.md @@ -1033,3 +1033,7 @@ After FOU-555: read `extern crate rand;` in a submodule. The path is longer, and the answer is the same. + +After the fixes for the review of PR 53, the measure gives the same +numbers. No project of the corpus renames a Cargo dependency, and no +function of a dependency has two platform twins with two result types.