Skip to content

Commit c3defe2

Browse files
Merge #5518
5518: Use resolved paths in SSR rules r=matklad a=davidlattimore The main user-visible changes are: * SSR now matches paths based on whether they resolve to the same thing instead of whether they're written the same. * So `foo()` won't match `foo()` if it's a different function `foo()`, but will match `bar::foo()` if it's the same `foo`. * Paths in the replacement will now be rendered with appropriate qualification for their context. * For example `foo::Bar` will render as just `Bar` inside the module `foo`, but might render as `baz::foo::Bar` from elsewhere. * This means that all paths in the search pattern and replacement template must be able to be resolved. * It now also matters where you invoke SSR from, since paths are resolved relative to wherever that is. * Search now uses find-uses on paths to locate places to try matching. This means that when a path is present in the pattern, search will generally be pretty fast. * Function calls can now match method calls again, but this time only if they resolve to the same function. Co-authored-by: David Lattimore <[email protected]>
2 parents 0e5095d + 58680cb commit c3defe2

File tree

20 files changed

+1431
-393
lines changed

20 files changed

+1431
-393
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/ra_ide/src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -505,9 +505,10 @@ impl Analysis {
505505
&self,
506506
query: &str,
507507
parse_only: bool,
508+
position: FilePosition,
508509
) -> Cancelable<Result<SourceChange, SsrError>> {
509510
self.with_db(|db| {
510-
let edits = ssr::parse_search_replace(query, parse_only, db)?;
511+
let edits = ssr::parse_search_replace(query, parse_only, db, position)?;
511512
Ok(SourceChange::from(edits))
512513
})
513514
}

crates/ra_ide/src/ssr.rs

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
use ra_db::SourceDatabaseExt;
2-
use ra_ide_db::{symbol_index::SymbolsDatabase, RootDatabase};
1+
use ra_db::FilePosition;
2+
use ra_ide_db::RootDatabase;
33

44
use crate::SourceFileEdit;
55
use ra_ssr::{MatchFinder, SsrError, SsrRule};
@@ -11,6 +11,19 @@ use ra_ssr::{MatchFinder, SsrError, SsrRule};
1111
// A `$<name>` placeholder in the search pattern will match any AST node and `$<name>` will reference it in the replacement.
1212
// Within a macro call, a placeholder will match up until whatever token follows the placeholder.
1313
//
14+
// All paths in both the search pattern and the replacement template must resolve in the context
15+
// in which this command is invoked. Paths in the search pattern will then match the code if they
16+
// resolve to the same item, even if they're written differently. For example if we invoke the
17+
// command in the module `foo` with a pattern of `Bar`, then code in the parent module that refers
18+
// to `foo::Bar` will match.
19+
//
20+
// Paths in the replacement template will be rendered appropriately for the context in which the
21+
// replacement occurs. For example if our replacement template is `foo::Bar` and we match some
22+
// code in the `foo` module, we'll insert just `Bar`.
23+
//
24+
// Method calls should generally be written in UFCS form. e.g. `foo::Bar::baz($s, $a)` will match
25+
// `$s.baz($a)`, provided the method call `baz` resolves to the method `foo::Bar::baz`.
26+
//
1427
// Placeholders may be given constraints by writing them as `${<name>:<constraint1>:<constraint2>...}`.
1528
//
1629
// Supported constraints:
@@ -43,21 +56,13 @@ pub fn parse_search_replace(
4356
rule: &str,
4457
parse_only: bool,
4558
db: &RootDatabase,
59+
position: FilePosition,
4660
) -> Result<Vec<SourceFileEdit>, SsrError> {
47-
let mut edits = vec![];
4861
let rule: SsrRule = rule.parse()?;
62+
let mut match_finder = MatchFinder::in_context(db, position);
63+
match_finder.add_rule(rule)?;
4964
if parse_only {
50-
return Ok(edits);
51-
}
52-
let mut match_finder = MatchFinder::new(db);
53-
match_finder.add_rule(rule);
54-
for &root in db.local_roots().iter() {
55-
let sr = db.source_root(root);
56-
for file_id in sr.iter() {
57-
if let Some(edit) = match_finder.edits_for_file(file_id) {
58-
edits.push(SourceFileEdit { file_id, edit });
59-
}
60-
}
65+
return Ok(Vec::new());
6166
}
62-
Ok(edits)
67+
Ok(match_finder.edits())
6368
}

crates/ra_ide_db/src/defs.rs

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -290,20 +290,25 @@ pub fn classify_name_ref(
290290

291291
let path = name_ref.syntax().ancestors().find_map(ast::Path::cast)?;
292292
let resolved = sema.resolve_path(&path)?;
293-
let res = match resolved {
294-
PathResolution::Def(def) => Definition::ModuleDef(def),
295-
PathResolution::AssocItem(item) => {
296-
let def = match item {
297-
hir::AssocItem::Function(it) => it.into(),
298-
hir::AssocItem::Const(it) => it.into(),
299-
hir::AssocItem::TypeAlias(it) => it.into(),
300-
};
301-
Definition::ModuleDef(def)
293+
Some(NameRefClass::Definition(resolved.into()))
294+
}
295+
296+
impl From<PathResolution> for Definition {
297+
fn from(path_resolution: PathResolution) -> Self {
298+
match path_resolution {
299+
PathResolution::Def(def) => Definition::ModuleDef(def),
300+
PathResolution::AssocItem(item) => {
301+
let def = match item {
302+
hir::AssocItem::Function(it) => it.into(),
303+
hir::AssocItem::Const(it) => it.into(),
304+
hir::AssocItem::TypeAlias(it) => it.into(),
305+
};
306+
Definition::ModuleDef(def)
307+
}
308+
PathResolution::Local(local) => Definition::Local(local),
309+
PathResolution::TypeParam(par) => Definition::TypeParam(par),
310+
PathResolution::Macro(def) => Definition::Macro(def),
311+
PathResolution::SelfType(impl_def) => Definition::SelfType(impl_def),
302312
}
303-
PathResolution::Local(local) => Definition::Local(local),
304-
PathResolution::TypeParam(par) => Definition::TypeParam(par),
305-
PathResolution::Macro(def) => Definition::Macro(def),
306-
PathResolution::SelfType(impl_def) => Definition::SelfType(impl_def),
307-
};
308-
Some(NameRefClass::Definition(res))
313+
}
309314
}

crates/ra_ide_db/src/search.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ impl SearchScope {
6060
SearchScope::new(std::iter::once((file, None)).collect())
6161
}
6262

63+
pub fn files(files: &[FileId]) -> SearchScope {
64+
SearchScope::new(files.iter().map(|f| (*f, None)).collect())
65+
}
66+
6367
pub fn intersection(&self, other: &SearchScope) -> SearchScope {
6468
let (mut small, mut large) = (&self.entries, &other.entries);
6569
if small.len() > large.len() {

crates/ra_ssr/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,6 @@ ra_ide_db = { path = "../ra_ide_db" }
1818
hir = { path = "../ra_hir", package = "ra_hir" }
1919
rustc-hash = "1.1.0"
2020
test_utils = { path = "../test_utils" }
21+
22+
[dev-dependencies]
23+
expect = { path = "../expect" }

0 commit comments

Comments
 (0)