db::{AstDatabase, DefDatabase, HirDatabase},
ids::{AstItemDef, LocationCtx},
name::AsName,
- Const, Crate, Enum, EnumVariant, FieldSource, Function, HasSource, ImplBlock, Module,
- ModuleSource, Source, Static, Struct, StructField, Trait, TypeAlias, Union, VariantDef,
+ AssocItem, Const, Crate, Enum, EnumVariant, FieldSource, Function, HasSource, ImplBlock,
+ Module, ModuleDef, ModuleSource, Source, Static, Struct, StructField, Trait, TypeAlias, Union,
+ VariantDef,
};
pub trait FromSource: Sized {
}
}
+impl FromSource for AssocItem {
+ type Ast = ast::ImplItem;
+ fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self> {
+ macro_rules! def {
+ ($kind:ident, $ast:ident) => {
+ $kind::from_source(db, Source { file_id: src.file_id, ast: $ast })
+ .and_then(|it| Some(AssocItem::from(it)))
+ };
+ }
+
+ match src.ast {
+ ast::ImplItem::FnDef(f) => def!(Function, f),
+ ast::ImplItem::ConstDef(c) => def!(Const, c),
+ ast::ImplItem::TypeAliasDef(a) => def!(TypeAlias, a),
+ }
+ }
+}
+
+// not fully matched
+impl FromSource for ModuleDef {
+ type Ast = ast::ModuleItem;
+ fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self> {
+ macro_rules! def {
+ ($kind:ident, $ast:ident) => {
+ $kind::from_source(db, Source { file_id: src.file_id, ast: $ast })
+ .and_then(|it| Some(ModuleDef::from(it)))
+ };
+ }
+
+ match src.ast {
+ ast::ModuleItem::FnDef(f) => def!(Function, f),
+ ast::ModuleItem::ConstDef(c) => def!(Const, c),
+ ast::ModuleItem::TypeAliasDef(a) => def!(TypeAlias, a),
+ ast::ModuleItem::TraitDef(t) => def!(Trait, t),
+ ast::ModuleItem::StaticDef(s) => def!(Static, s),
+ ast::ModuleItem::StructDef(s) => {
+ let src = Source { file_id: src.file_id, ast: s };
+ let s = Struct::from_source(db, src)?;
+ Some(ModuleDef::Adt(s.into()))
+ }
+ ast::ModuleItem::EnumDef(e) => {
+ let src = Source { file_id: src.file_id, ast: e };
+ let e = Enum::from_source(db, src)?;
+ Some(ModuleDef::Adt(e.into()))
+ }
+ ast::ModuleItem::Module(ref m) if !m.has_semi() => {
+ let src = Source { file_id: src.file_id, ast: ModuleSource::Module(m.clone()) };
+ let module = Module::from_definition(db, src)?;
+ Some(ModuleDef::Module(module))
+ }
+ _ => None,
+ }
+ }
+}
+
// FIXME: simplify it
impl ModuleSource {
pub fn from_position(
use crate::{
db::RootDatabase,
display::ShortLabel,
- name_ref_kind::{classify_name_ref, NameRefKind::*},
+ name_ref_kind::{classify_name_ref, NameKind::*},
FilePosition, NavigationTarget, RangeInfo,
};
description_from_symbol, docs_from_symbol, macro_label, rust_code_markup,
rust_code_markup_with_doc, ShortLabel,
},
- name_ref_kind::{classify_name_ref, NameRefKind::*},
+ name_ref_kind::{classify_name_ref, NameKind::*},
FilePosition, FileRange, RangeInfo,
};
use crate::db::RootDatabase;
-pub enum NameRefKind {
+pub enum NameKind {
Method(hir::Function),
Macro(hir::MacroDef),
FieldAccess(hir::StructField),
db: &RootDatabase,
analyzer: &hir::SourceAnalyzer,
name_ref: &ast::NameRef,
-) -> Option<NameRefKind> {
- use NameRefKind::*;
+) -> Option<NameKind> {
+ use NameKind::*;
// Check if it is a method
if let Some(method_call) = name_ref.syntax().parent().and_then(ast::MethodCallExpr::cast) {
//! FIXME: write short doc here
-use hir::{Either, ModuleSource};
+use hir::{FromSource, ModuleSource};
use ra_db::{SourceDatabase, SourceDatabaseExt};
-use ra_syntax::{algo::find_node_at_offset, ast, AstNode, SourceFile, SyntaxNode};
+use ra_syntax::{algo::find_node_at_offset, ast, AstNode, AstPtr, SyntaxNode};
use relative_path::{RelativePath, RelativePathBuf};
use crate::{
- db::RootDatabase, FileId, FilePosition, FileRange, FileSystemEdit, NavigationTarget, RangeInfo,
- SourceChange, SourceFileEdit, TextRange,
+ db::RootDatabase,
+ name_ref_kind::{
+ classify_name_ref,
+ NameKind::{self, *},
+ },
+ FileId, FilePosition, FileRange, FileSystemEdit, NavigationTarget, RangeInfo, SourceChange,
+ SourceFileEdit, TextRange,
};
#[derive(Debug, Clone)]
position: FilePosition,
) -> Option<RangeInfo<ReferenceSearchResult>> {
let parse = db.parse(position.file_id);
- let RangeInfo { range, info: (binding, analyzer) } = find_binding(db, &parse.tree(), position)?;
- let declaration = NavigationTarget::from_bind_pat(position.file_id, &binding);
+ let syntax = parse.tree().syntax().clone();
+ let RangeInfo { range, info: (analyzer, name_kind) } = find_name(db, &syntax, position)?;
+
+ let declaration = match name_kind {
+ Macro(mac) => NavigationTarget::from_macro_def(db, mac),
+ FieldAccess(field) => NavigationTarget::from_field(db, field),
+ AssocItem(assoc) => NavigationTarget::from_assoc_item(db, assoc),
+ Method(func) => NavigationTarget::from_def_source(db, func),
+ Def(def) => NavigationTarget::from_def(db, def)?,
+ SelfType(ref ty) => match ty.as_adt() {
+ Some((def_id, _)) => NavigationTarget::from_adt_def(db, def_id),
+ None => return None,
+ },
+ Pat(pat) => NavigationTarget::from_pat(db, position.file_id, pat),
+ SelfParam(par) => NavigationTarget::from_self_param(position.file_id, par),
+ GenericParam(_) => return None,
+ };
- let references = analyzer
- .find_all_refs(&binding)
- .into_iter()
- .map(move |ref_desc| FileRange { file_id: position.file_id, range: ref_desc.range })
- .collect::<Vec<_>>();
+ let references = match name_kind {
+ Pat(pat) => analyzer
+ .find_all_refs(&pat.to_node(&syntax))
+ .into_iter()
+ .map(move |ref_desc| FileRange { file_id: position.file_id, range: ref_desc.range })
+ .collect::<Vec<_>>(),
+ _ => vec![],
+ };
return Some(RangeInfo::new(range, ReferenceSearchResult { declaration, references }));
- fn find_binding<'a>(
+ fn find_name<'a>(
db: &RootDatabase,
- source_file: &SourceFile,
+ syntax: &SyntaxNode,
position: FilePosition,
- ) -> Option<RangeInfo<(ast::BindPat, hir::SourceAnalyzer)>> {
- let syntax = source_file.syntax();
- if let Some(binding) = find_node_at_offset::<ast::BindPat>(syntax, position.offset) {
- let range = binding.syntax().text_range();
- let analyzer = hir::SourceAnalyzer::new(db, position.file_id, binding.syntax(), None);
- return Some(RangeInfo::new(range, (binding, analyzer)));
- };
- let name_ref = find_node_at_offset::<ast::NameRef>(syntax, position.offset)?;
+ ) -> Option<RangeInfo<(hir::SourceAnalyzer, NameKind)>> {
+ if let Some(name) = find_node_at_offset::<ast::Name>(&syntax, position.offset) {
+ let analyzer = hir::SourceAnalyzer::new(db, position.file_id, name.syntax(), None);
+ let name_kind = classify_name(db, position.file_id, &name)?;
+ let range = name.syntax().text_range();
+ return Some(RangeInfo::new(range, (analyzer, name_kind)));
+ }
+ let name_ref = find_node_at_offset::<ast::NameRef>(&syntax, position.offset)?;
let range = name_ref.syntax().text_range();
let analyzer = hir::SourceAnalyzer::new(db, position.file_id, name_ref.syntax(), None);
- let resolved = analyzer.resolve_local_name(&name_ref)?;
- if let Either::A(ptr) = resolved.ptr() {
- if let ast::Pat::BindPat(binding) = ptr.to_node(source_file.syntax()) {
- return Some(RangeInfo::new(range, (binding, analyzer)));
- }
- }
- None
+ let name_kind = classify_name_ref(db, &analyzer, &name_ref)?;
+ Some(RangeInfo::new(range, (analyzer, name_kind)))
}
}
+fn classify_name(db: &RootDatabase, file_id: FileId, name: &ast::Name) -> Option<NameKind> {
+ let parent = name.syntax().parent()?;
+ let file_id = file_id.into();
+
+ if let Some(pat) = ast::BindPat::cast(parent.clone()) {
+ return Some(Pat(AstPtr::new(&pat)));
+ }
+ if let Some(var) = ast::EnumVariant::cast(parent.clone()) {
+ let src = hir::Source { file_id, ast: var };
+ let var = hir::EnumVariant::from_source(db, src)?;
+ return Some(Def(var.into()));
+ }
+ if let Some(field) = ast::RecordFieldDef::cast(parent.clone()) {
+ let src = hir::Source { file_id, ast: hir::FieldSource::Named(field) };
+ let field = hir::StructField::from_source(db, src)?;
+ return Some(FieldAccess(field));
+ }
+ if let Some(field) = ast::TupleFieldDef::cast(parent.clone()) {
+ let src = hir::Source { file_id, ast: hir::FieldSource::Pos(field) };
+ let field = hir::StructField::from_source(db, src)?;
+ return Some(FieldAccess(field));
+ }
+ if let Some(_) = parent.parent().and_then(ast::ItemList::cast) {
+ let ast = ast::ImplItem::cast(parent.clone())?;
+ let src = hir::Source { file_id, ast };
+ let item = hir::AssocItem::from_source(db, src)?;
+ return Some(AssocItem(item));
+ }
+ if let Some(item) = ast::ModuleItem::cast(parent.clone()) {
+ let src = hir::Source { file_id, ast: item };
+ let def = hir::ModuleDef::from_source(db, src)?;
+ return Some(Def(def));
+ }
+ // FIXME: TYPE_PARAM, ALIAS, MACRO_CALL; Union
+
+ None
+}
+
pub(crate) fn rename(
db: &RootDatabase,
position: FilePosition,
assert_eq!(refs.len(), 2);
}
+ #[test]
+ fn test_find_all_refs_field_name() {
+ let code = r#"
+ //- /lib.rs
+ struct Foo {
+ spam<|>: u32,
+ }
+ "#;
+
+ let refs = get_all_refs(code);
+ assert_eq!(refs.len(), 1);
+ }
+
+ #[test]
+ fn test_find_all_refs_impl_item_name() {
+ let code = r#"
+ //- /lib.rs
+ struct Foo;
+ impl Foo {
+ fn f<|>(&self) { }
+ }
+ "#;
+
+ let refs = get_all_refs(code);
+ assert_eq!(refs.len(), 1);
+ }
+
+ #[test]
+ fn test_find_all_refs_enum_var_name() {
+ let code = r#"
+ //- /lib.rs
+ enum Foo {
+ A,
+ B<|>,
+ C,
+ }
+ "#;
+
+ let refs = get_all_refs(code);
+ assert_eq!(refs.len(), 1);
+ }
+
fn get_all_refs(text: &str) -> ReferenceSearchResult {
let (analysis, position) = single_file_with_position(text);
analysis.find_all_refs(position).unwrap().unwrap()
--- /dev/null
+pub enum SearchScope {
+ Function(hir::Function),
+ Module(hir::Module),
+ Crate(hir::Crate),
+ Crates(Vec<hir::Crate>),
+}
+
+pub struct SearchScope{
+ pub scope: Vec<SyntaxNode>
+}
+
+pub fn find_all_refs(db: &RootDatabase, decl: NameKind) -> Vec<ReferenceDescriptor> {
+ let (module, visibility) = match decl {
+ FieldAccess(field) => {
+ let parent = field.parent_def(db);
+ let module = parent.module(db);
+ let visibility = match parent {
+ VariantDef::Struct(s) => s.source(db).ast.visibility(),
+ VariantDef::EnumVariant(v) => v.parent_enum(db).source(db).ast.visibility(),
+ };
+ (module, visibility)
+ }
+ AssocItem(item) => {
+ let parent = item.parent_trait(db)?;
+ let module = parent.module(db);
+ let visibility = parent.source(db).ast.visibility();
+ (module, visibility)
+ }
+ Def(def) => {
+ let (module, visibility) = match def {
+ ModuleDef::Module(m) => (m, ),
+ ModuleDef::Function(f) => (f.module(db), f.source(db).ast.visibility()),
+ ModuleDef::Adt::Struct(s) => (s.module(db), s.source(db).ast.visibility()),
+ ModuleDef::Adt::Union(u) => (u.module(db), u.source(db).ast.visibility()),
+ ModuleDef::Adt::Enum(e) => (e.module(db), e.source(db).ast.visibility()),
+ ModuleDef::EnumVariant(v) => (v.module(db), v.source(db).ast.visibility()),
+ ModuleDef::Const(c) => (c.module(db), c.source(db).ast.visibility()),
+ ModuleDef::Static(s) => (s.module(db), s.source(db).ast.visibility()),
+ ModuleDef::Trait(t) => (t.module(db), t.source(db).ast.visibility()),
+ ModuleDef::TypeAlias(a) => (a.module(db), a.source(db).ast.visibility()),
+ ModuleDef::BuiltinType(_) => return vec![];
+ };
+ (module, visibility)
+ }
+ // FIXME: add missing kinds
+ _ => return vec![];
+ };
+ let scope = scope(db, module, visibility);
+}
+
+fn scope(db: &RootDatabase, module: hir::Module, item_vis: Option<ast::Visibility>) -> SearchScope {
+ if let Some(v) = item_vis {
+ let krate = module.krate(db)?;
+
+ if v.syntax().text() == "pub" {
+ SearchScope::Crate(krate)
+ }
+ if v.syntax().text() == "pub(crate)" {
+ let crate_graph = db.crate_graph();
+ let crates = crate_graph.iter().filter(|id| {
+ crate_graph.dependencies(id).any(|d| d.crate_id() == krate.crate_id())
+ }).map(|id| Crate { id }).collect::<Vec<_>>();
+ crates.insert(0, krate);
+ SearchScope::Crates(crates)
+ }
+ // FIXME: "pub(super)", "pub(in path)"
+ SearchScope::Module(module)
+ }
+ SearchScope::Module(module)
+}
+
+fn process_one(db, scope: SearchScope, pat) {
+ match scope {
+ SearchScope::Crate(krate) => {
+ let text = db.file_text(position.file_id).as_str();
+ let parse = SourceFile::parse(text);
+ for (offset, name) in text.match_indices(pat) {
+ if let Some() = find_node_at_offset<ast::NameRef>(parse, offset) {
+
+ }
+ }
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::{
+ mock_analysis::analysis_and_position, mock_analysis::single_file_with_position, FileId,
+ ReferenceSearchResult,
+ };
+ use insta::assert_debug_snapshot;
+ use test_utils::assert_eq_text;
+
+ #[test]
+ fn test_find_all_refs_for_local() {
+ let code = r#"
+ fn main() {
+ let mut i = 1;
+ let j = 1;
+ i = i<|> + j;
+
+ {
+ i = 0;
+ }
+
+ i = 5;
+ }
+ "#;
+
+ let refs = get_all_refs(code);
+ assert_eq!(refs.len(), 5);
+ }
+
+ #[test]
+ fn test_find_all_refs_for_param_inside() {
+ let code = r#"
+ fn foo(i : u32) -> u32 {
+ i<|>
+ }"#;
+
+ let refs = get_all_refs(code);
+ assert_eq!(refs.len(), 2);
+ }
+
+ #[test]
+ fn test_find_all_refs_for_fn_param() {
+ let code = r#"
+ fn foo(i<|> : u32) -> u32 {
+ i
+ }"#;
+
+ let refs = get_all_refs(code);
+ assert_eq!(refs.len(), 2);
+ }
+
+ #[test]
+ fn test_find_all_refs_field_name() {
+ let code = r#"
+ //- /lib.rs
+ struct Foo {
+ spam<|>: u32,
+ }
+ "#;
+
+ let refs = get_all_refs(code);
+ assert_eq!(refs.len(), 1);
+ }
+
+ #[test]
+ fn test_find_all_refs_methods() {
+ let code = r#"
+ //- /lib.rs
+ struct Foo;
+ impl Foo {
+ pub fn a() {
+ self.b()
+ }
+ fn b(&self) {}
+ }
+ "#;
+
+ let refs = get_all_refs(code);
+ assert_eq!(refs.len(), 1);
+ }
+
+ #[test]
+ fn test_find_all_refs_pub_enum() {
+ let code = r#"
+ //- /lib.rs
+ pub enum Foo {
+ A,
+ B<|>,
+ C,
+ }
+ "#;
+
+ let refs = get_all_refs(code);
+ assert_eq!(refs.len(), 1);
+ }
+
+ fn get_all_refs(text: &str) -> ReferenceSearchResult {
+ let (analysis, position) = single_file_with_position(text);
+ analysis.find_all_refs(position).unwrap().unwrap()
+ }
+}
\ No newline at end of file
use crate::{
db::RootDatabase,
- name_ref_kind::{classify_name_ref, NameRefKind::*},
+ name_ref_kind::{classify_name_ref, NameKind::*},
FileId,
};