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
20 changes: 19 additions & 1 deletion crates/ide/src/display/navigation_target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use ide_db::{defs::Definition, RootDatabase};
use syntax::{
ast::{self, NameOwner},
match_ast, AstNode, SmolStr,
SyntaxKind::{self, IDENT_PAT, TYPE_PARAM},
SyntaxKind::{self, IDENT_PAT, LIFETIME_PARAM, TYPE_PARAM},
TextRange,
};

Expand Down Expand Up @@ -182,6 +182,7 @@ impl TryToNav for Definition {
Definition::SelfType(it) => Some(it.to_nav(db)),
Definition::Local(it) => Some(it.to_nav(db)),
Definition::TypeParam(it) => Some(it.to_nav(db)),
Definition::LifetimeParam(it) => Some(it.to_nav(db)),
}
}
}
Expand Down Expand Up @@ -376,6 +377,23 @@ impl ToNav for hir::TypeParam {
}
}

impl ToNav for hir::LifetimeParam {
fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
let src = self.source(db);
let full_range = src.value.syntax().text_range();
NavigationTarget {
file_id: src.file_id.original_file(db),
name: self.name(db).to_string().into(),
kind: LIFETIME_PARAM,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrelated to PR, but, now that I look at it, using SyntaxKind here is abstraction sloppiness. This really should be enum DefinitionKind declated in the ide crate, and re-used by HighlightTag enum. Will probably look into fixing that.

full_range,
focus_range: Some(full_range),
container_name: None,
description: None,
docs: None,
}
}
}

pub(crate) fn docs_from_symbol(db: &RootDatabase, symbol: &FileSymbol) -> Option<Documentation> {
let parse = db.parse(symbol.file_id);
let node = symbol.ptr.to_node(parse.tree().syntax());
Expand Down
5 changes: 4 additions & 1 deletion crates/ide/src/doc_links.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,10 @@ fn rewrite_intra_doc_link(
},
Definition::Macro(it) => it.resolve_doc_path(db, link, ns),
Definition::Field(it) => it.resolve_doc_path(db, link, ns),
Definition::SelfType(_) | Definition::Local(_) | Definition::TypeParam(_) => return None,
Definition::SelfType(_)
| Definition::Local(_)
| Definition::TypeParam(_)
| Definition::LifetimeParam(_) => return None,
}?;
let krate = resolved.module(db)?.krate();
let canonical_path = resolved.canonical_path(db)?;
Expand Down
57 changes: 49 additions & 8 deletions crates/ide/src/goto_definition.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use either::Either;
use hir::Semantics;
use ide_db::{
base_db::FileId,
Expand Down Expand Up @@ -33,7 +34,7 @@ pub(crate) fn goto_definition(
let nav_targets = match_ast! {
match parent {
ast::NameRef(name_ref) => {
reference_definition(&sema, &name_ref).to_vec()
reference_definition(&sema, Either::Right(&name_ref)).to_vec()
},
ast::Name(name) => {
let def = NameClass::classify(&sema, &name)?.referenced_or_defined(sema.db);
Expand All @@ -53,6 +54,13 @@ pub(crate) fn goto_definition(
let self_param = func.param_list()?.self_param()?;
vec![self_to_nav_target(self_param, position.file_id)?]
},
ast::Lifetime(lt) => if let Some(name_class) = NameClass::classify_lifetime(&sema, &lt) {
let def = name_class.referenced_or_defined(sema.db);
let nav = def.try_to_nav(sema.db)?;
vec![nav]
} else {
reference_definition(&sema, Either::Left(&lt)).to_vec()
},
_ => return None,
}
};
Expand All @@ -64,7 +72,7 @@ fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
return tokens.max_by_key(priority);
fn priority(n: &SyntaxToken) -> usize {
match n.kind() {
IDENT | INT_NUMBER | T![self] => 2,
IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] => 2,
kind if kind.is_trivia() => 0,
_ => 1,
}
Expand Down Expand Up @@ -102,9 +110,12 @@ impl ReferenceResult {

pub(crate) fn reference_definition(
sema: &Semantics<RootDatabase>,
name_ref: &ast::NameRef,
name_ref: Either<&ast::Lifetime, &ast::NameRef>,
) -> ReferenceResult {
let name_kind = NameRefClass::classify(sema, name_ref);
let name_kind = name_ref.either(
|lifetime| NameRefClass::classify_lifetime(sema, lifetime),
|name_ref| NameRefClass::classify(sema, name_ref),
);
if let Some(def) = name_kind {
let def = def.referenced(sema.db);
return match def.try_to_nav(sema.db) {
Expand All @@ -114,10 +125,9 @@ pub(crate) fn reference_definition(
}

// Fallback index based approach:
let navs = symbol_index::index_resolve(sema.db, name_ref)
.into_iter()
.map(|s| s.to_nav(sema.db))
.collect();
let name = name_ref.either(ast::Lifetime::text, ast::NameRef::text);
let navs =
symbol_index::index_resolve(sema.db, name).into_iter().map(|s| s.to_nav(sema.db)).collect();
ReferenceResult::Approximate(navs)
}

Expand Down Expand Up @@ -1033,6 +1043,37 @@ impl Foo {
fn bar(&self<|>) {
//^^^^
}
}"#,
)
}

#[test]
fn goto_lifetime_param_on_decl() {
check(
r#"
fn foo<'foobar<|>>(_: &'foobar ()) {
//^^^^^^^
}"#,
)
}

#[test]
fn goto_lifetime_param_decl() {
check(
r#"
fn foo<'foobar>(_: &'foobar<|> ()) {
//^^^^^^^
}"#,
)
}

#[test]
fn goto_lifetime_param_decl_nested() {
check(
r#"
fn foo<'foobar>(_: &'foobar ()) {
fn foo<'foobar>(_: &'foobar<|> ()) {}
//^^^^^^^
}"#,
)
}
Expand Down
2 changes: 1 addition & 1 deletion crates/ide/src/hover.rs
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,7 @@ fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> {
Adt::Enum(it) => from_def_source(db, it, mod_path),
})
}
Definition::TypeParam(_) => {
Definition::TypeParam(_) | Definition::LifetimeParam(_) => {
// FIXME: Hover for generic param
None
}
Expand Down
7 changes: 7 additions & 0 deletions crates/ide/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -528,6 +528,13 @@ impl Analysis {
self.with_db(|db| references::rename::rename(db, position, new_name))
}

pub fn prepare_rename(
&self,
position: FilePosition,
) -> Cancelable<Result<RangeInfo<()>, RenameError>> {
self.with_db(|db| references::rename::prepare_rename(db, position))
}

pub fn structural_search_replace(
&self,
query: &str,
Expand Down
91 changes: 86 additions & 5 deletions crates/ide/src/references.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,8 @@ pub(crate) fn find_all_refs(
kind = ReferenceKind::FieldShorthandForLocal;
}
}
} else if let Definition::LifetimeParam(_) = def {
kind = ReferenceKind::Lifetime;
};

let declaration = Declaration { nav, kind, access: decl_access(&def, &syntax, decl_range) };
Expand All @@ -148,11 +150,29 @@ fn find_name(
let range = name.syntax().text_range();
return Some(RangeInfo::new(range, def));
}
let name_ref =
sema.find_node_at_offset_with_descend::<ast::NameRef>(&syntax, position.offset)?;
let def = NameRefClass::classify(sema, &name_ref)?.referenced(sema.db);
let range = name_ref.syntax().text_range();
Some(RangeInfo::new(range, def))

let (text_range, def) = if let Some(lifetime) =
sema.find_node_at_offset_with_descend::<ast::Lifetime>(&syntax, position.offset)
{
if let Some(def) = NameRefClass::classify_lifetime(sema, &lifetime)
.map(|class| NameRefClass::referenced(class, sema.db))
{
(lifetime.syntax().text_range(), def)
} else {
(
lifetime.syntax().text_range(),
NameClass::classify_lifetime(sema, &lifetime)?.referenced_or_defined(sema.db),
)
}
} else {
let name_ref =
sema.find_node_at_offset_with_descend::<ast::NameRef>(&syntax, position.offset)?;
(
name_ref.syntax().text_range(),
NameRefClass::classify(sema, &name_ref)?.referenced(sema.db),
)
};
Some(RangeInfo::new(text_range, def))
}

fn decl_access(def: &Definition, syntax: &SyntaxNode, range: TextRange) -> Option<ReferenceAccess> {
Expand Down Expand Up @@ -1005,4 +1025,65 @@ impl Foo {
}
expect.assert_eq(&actual)
}

#[test]
fn test_find_lifetimes_function() {
check(
r#"
trait Foo<'a> {}
impl<'a> Foo<'a> for &'a () {}
fn foo<'a, 'b: 'a>(x: &'a<|> ()) -> &'a () where &'a (): Foo<'a> {
fn bar<'a>(_: &'a ()) {}
x
}
"#,
expect![[r#"
'a LIFETIME_PARAM FileId(0) 55..57 55..57 Lifetime

FileId(0) 63..65 Lifetime
FileId(0) 71..73 Lifetime
FileId(0) 82..84 Lifetime
FileId(0) 95..97 Lifetime
FileId(0) 106..108 Lifetime
"#]],
);
}

#[test]
fn test_find_lifetimes_type_alias() {
check(
r#"
type Foo<'a, T> where T: 'a<|> = &'a T;
"#,
expect![[r#"
'a LIFETIME_PARAM FileId(0) 9..11 9..11 Lifetime

FileId(0) 25..27 Lifetime
FileId(0) 31..33 Lifetime
"#]],
);
}

#[test]
fn test_find_lifetimes_trait_impl() {
check(
r#"
trait Foo<'a> {
fn foo() -> &'a ();
}
impl<'a> Foo<'a> for &'a () {
fn foo() -> &'a<|> () {
unimplemented!()
}
}
"#,
expect![[r#"
'a LIFETIME_PARAM FileId(0) 47..49 47..49 Lifetime

FileId(0) 55..57 Lifetime
FileId(0) 64..66 Lifetime
FileId(0) 89..91 Lifetime
"#]],
);
}
}
Loading