Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ and adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
- In Go, calls through a method receiver or parameter of an unexported type, the usual shape of gRPC and HTTP handlers (`s.service.AddItem()` inside `func (s *server) Create()`), now resolve, and always within that type's own package: another package's `server` with a same-named method no longer takes the call, and a method the type gets from a struct or interface it embeds is found too. Re-index Go projects after upgrading. Thanks @GoDiao for the report and the fix. (#2323)
- In Go, calls into a module whose `go.mod` is not at the project root now resolve, whether it is a `server/` backend next to a `web/` frontend or one of several modules side by side as in etcd, so calls like `store.New()` and `s.db.CreateItem()` find their targets instead of being treated as calls into a third-party package. A name written through a package, like a `job.OPCommand` result type or a field of type `artifact.Manager`, now links to that package's symbol rather than a same-named one elsewhere, which also corrects links in projects with a single `go.mod`. Re-index Go projects after upgrading. Thanks @GoDiao for the report and @danusha2345 for the fix. (#2322)
- In Go, an exported type written without a package, like `Node` in `func Walk(v Visitor, node Node)`, now links to the type of that name in its own package, whether it is a struct, an interface or another kind of type. Before, it could link to a struct of the same name in an unrelated package, so prometheus's PromQL parser functions pointed at the Kubernetes discovery `Node` struct instead of the parser's own `Node` interface, or, for a result type like `Appender` in `func (f *fanout) Appender(…) Appender`, to the method itself. A type from a package outside your project, like `apiv1.Node` or `http.Handler`, no longer links to a struct of the same name in the file that uses it; a variadic parameter like `...storage.Filter` now links to that package's type; a route handler written as a method value, like `h.Follow` in an Echo or Gin app, links to that method rather than a model struct named `Follow`; and a method called on another call's result or on an indexed value, like `err[i].Error()` or a chained `.String()`, is no longer recorded as creating a struct of the same name. Re-index Go projects after upgrading.
- In Go, a function or method passed as a value, like `sync.Pool{New: wm.new}`, now shows the function that passes it among its callers and impact even when it is named `new`, `nil`, `None`, `self` or a handful of similar words, or when a line break or comment sits beside the dot. Before, such a value was skipped, so a method used only that way looked unused. Re-index Go projects after upgrading.
- Indexing a project that includes large bundled JavaScript files, such as a copy of pdf.js or d3, is fast again: since 1.6.2, resolving the calls in a JavaScript or TypeScript file re-read the file's text above each call, so a single bundled library could add many seconds to an index. The graph it builds is unchanged. Thanks @bompus for the report. (#2334)
- The time `codegraph init` and `codegraph index` print beside the node and edge counts now covers the whole run, resolving references and linking included, and `codegraph sync` reports its whole run the same way. Before, it counted only reading and parsing the files, which can be a small part of an index, so a slow index looked fast. Thanks @bompus for the report. (#2334)
- In Rust, code that uses an enum only through its variants, like `mode::Mode::A` in an expression, a `Mode::B =>` match arm or `Self::A` inside the enum's own `impl`, now shows up in that enum's callers and impact, linked to the enum that actually declares the variant rather than to a same-named type elsewhere. Standard-library variants like `Ordering::Less` or `Option::Some` and associated items like `Foo::new()` or `Foo::MAX` don't count as using a project enum. Re-index Rust projects after upgrading. Thanks @mg-mg-mg for the report and @danusha2345. (#2328)
Expand Down
24 changes: 24 additions & 0 deletions __tests__/kernel-tsjs-parity.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,30 @@ class Consumer:
expect(result.unresolvedReferences.some(r => r.referenceKind === 'calls' && r.referenceName === 'c.Fetch')).toBe(true);
});

it.each(['LF', 'CRLF'])('Go selector values stoplist the whole name, not the field (%s)', (ending) => {
// The stoplist holds bare words like Python's `None`; `raft.None` (etcd)
// and cgo's `C.NULL` are still values. The name is operand + field, so a
// break or comment after the dot keeps it; one inside the operand doesn't.
const source = `package demo
import "example.com/raft"
type Store struct{}
func (s *Store) new() any { return nil }
func wire(s *Store) {
Submit(raft.None, C.NULL, None, nil)
pool := sync.Pool{New: s.new}
Submit(raft.
Next, raft./* c */ Prev, raft.// c
Last)
Submit(s.
inner.Fetch)
_ = pool
}
`;
const result = assertParity('values.go', ending === 'CRLF' ? source.replace(/\n/g, '\r\n') : source, 'go');
const names = result.unresolvedReferences.filter(r => r.referenceKind === 'function_ref').map(r => r.referenceName);
expect(names.sort()).toEqual(['C.NULL', 'raft.Last', 'raft.Next', 'raft.None', 'raft.Prev', 's.new']);
});

it.each(REAL_SOURCES)('real source parity: %s', (rel) => {
const file = path.join(__dirname, '..', rel);
assertParity(rel, fs.readFileSync(file, 'utf8'), 'typescript');
Expand Down
24 changes: 13 additions & 11 deletions codegraph-kernel/src/go.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1028,26 +1028,28 @@ impl<'t> Walker<'t> {
row: p.row,
});
}
// #1820: preserve the receiver of a method value.
// #1820: preserve the receiver of a method value. The name is
// rebuilt from operand and field, as normalizeSpecial does, so a
// comment or line break beside the dot keeps the candidate.
// NAME_STOPLIST applies to the whole name and holds no dotted
// word, so it never drops one: `raft.None` is a candidate even
// though a bare `None` is not.
"selector_expression" => {
let field = v
.child_by_field_name("field")
.or_else(|| v.named_child(v.named_child_count().saturating_sub(1)));
let Some(field) = field else { return };
let name = self.text(field);
if name.is_empty() || is_stoplisted(name) {
let (Some(operand), Some(field)) =
(v.child_by_field_name("operand"), v.child_by_field_name("field"))
else {
return;
}
let value = self.text(v);
if !value.split('.').all(|part| {
};
let name = format!("{}.{}", self.text(operand), self.text(field));
if !name.split('.').all(|part| {
!part.is_empty() && part.chars().enumerate().all(|(i, c)| {
c == '_' || c.is_ascii_alphabetic() || (i > 0 && c.is_ascii_digit())
})
}) { return; }
let p = field.start_position();
self.fn_ref_cands.push(Cand {
from,
name: value.to_string(),
name,
line: p.row as u32 + 1,
column_byte: field.start_byte(),
row: p.row,
Expand Down