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
42 changes: 35 additions & 7 deletions strategies/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,9 @@ func joinQualified(qualifier, name string) string {
return name
}
// Identifier tokens can't contain whitespace; reject anything that
// slipped through except the deliberate "name()" call-result marker.
bare := strings.TrimSuffix(qualifier, "()")
// slipped through except the deliberate "name()" call-result marker
// (and Rust's "name()?", the result after `?`).
bare := strings.TrimSuffix(strings.TrimSuffix(qualifier, "?"), "()")
if strings.ContainsAny(bare, " \t\n().") {
return name
}
Expand Down Expand Up @@ -778,7 +779,7 @@ func rustCallSites(body *sitter.Node, src []byte) []astkit.CallSite {
map[string]string{"field_expression": "field", "scoped_identifier": "name"},
map[string]string{"call_expression": "function"})
if qual == "" {
qual = rustExprReceiver(value)
qual = rustExprReceiver(value, src)
}
return joinQualified(qual, string(field.Content(src)))
}
Expand Down Expand Up @@ -864,6 +865,8 @@ func rustMacroCallSites(body *sitter.Node, src []byte) []astkit.CallSite {
qual = rustLastSegment(path[:i])
} else if m[0] >= 2 && raw[m[0]-1] == '.' && raw[m[0]-2] == '"' {
qual = rustStrReceiver // `"match:fg:magenta".parse()` in vec![...]
} else if m[0] >= 1 && raw[m[0]-1] == '.' && rustMacroChainQualifier(text, m[0]) == "" {
qual = rustExprReceiverMark // `dents[0].path_is_symlink()`: a method on something
} else if q := rustMacroChainQualifier(text, m[0]); q != "" {
// `ig.matched("", false).is_ignore()`: the regex
// scan (no nested-structure awareness) matches
Expand Down Expand Up @@ -1866,11 +1869,12 @@ func findChildByType(n *sitter.Node, typeName string) *sitter.Node {
// every same-named function and method (ripgrep: "test".as_bytes(),
// doc[start..].find(...)).
const (
rustStrReceiver = "<str>"
rustSliceReceiver = "<slice>"
rustStrReceiver = "<str>"
rustSliceReceiver = "<slice>"
rustExprReceiverMark = "<expr>"
)

func rustExprReceiver(value *sitter.Node) string {
func rustExprReceiver(value *sitter.Node, src []byte) string {
if value == nil {
return ""
}
Expand All @@ -1881,6 +1885,30 @@ func rustExprReceiver(value *sitter.Node) string {
if internalast.FindChildByType(value, "range_expression") != nil {
return rustSliceReceiver
}
return rustExprReceiverMark
case "try_expression":
// `args.walk_builder()?.build_parallel()`: the receiver is the
// call's result after `?`, written "walk_builder()?".
if inner := value.NamedChild(0); inner != nil && inner.Type() == "call_expression" {
if fn := inner.ChildByFieldName("function"); fn != nil {
name := fn.Content(src)
if i := strings.LastIndexAny(name, ".:"); i >= 0 {
name = name[i+1:]
}
if name != "" {
return name + "()?" // the `?` unwraps its Result/Option
}
}
}
case "parenthesized_expression", "unary_expression", "reference_expression":
// `(*self).find(..)`, `(**self).begin(..)`: a dereferenced self
// is self (ripgrep's blanket impls forward this way).
inner := strings.TrimLeft(strings.Trim(strings.TrimSpace(value.Content(src)), "()"), "*& ")
if inner == "self" {
return "self"
}
}
return ""
// Any other expression receiver (`x()?.m()`, `(a + b).m()`): a method
// call on a value of unknown type — never a bare call to a function.
return rustExprReceiverMark
}
6 changes: 5 additions & 1 deletion strategies/metadata_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1488,6 +1488,10 @@ func TestRust_LiteralAndSliceReceiversAreMarked(t *testing.T) {
let a = "test".as_bytes();
let b = doc[start..].find('}');
let c = vec!["x".parse().unwrap()];
let d = (*self).find(doc);
let e = args.walk_builder()?.build_parallel();
assert!(!dents[0].path_is_symlink());
let f = paths[0].is_dir();
}
`
syms, _ := extract(t, astkit.LangRust, src)
Expand All @@ -1497,7 +1501,7 @@ func TestRust_LiteralAndSliceReceiversAreMarked(t *testing.T) {
got[cs.Callee] = true
}
}
for _, want := range []string{"<str>.as_bytes", "<slice>.find", "<str>.parse"} {
for _, want := range []string{"<str>.as_bytes", "<slice>.find", "<str>.parse", "self.find", "walk_builder()?.build_parallel", "<expr>.path_is_symlink", "<expr>.is_dir"} {
if !got[want] {
t.Errorf("missing %q in %v", want, got)
}
Expand Down
Loading