From a1c96e04be55b3412e5510fc8d09cd82675dd4cd Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sun, 14 Mar 2021 16:12:38 +0100 Subject: Introduce Semantics::visit_file_defs --- crates/hir/src/semantics.rs | 26 ++++++++++++- crates/ide/src/annotations.rs | 86 +++++++++++++++++++++++++++++-------------- crates/ide/src/runnables.rs | 59 ++++++++++------------------- 3 files changed, 103 insertions(+), 68 deletions(-) diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index 945638cc5..2a0a36de4 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -2,10 +2,12 @@ mod source_to_def; -use std::{cell::RefCell, fmt, iter::successors}; +use std::{cell::RefCell, collections::VecDeque, fmt, iter::successors}; use base_db::{FileId, FileRange}; +use either::Either; use hir_def::{ + nameres::ModuleSource, resolver::{self, HasResolver, Resolver, TypeNs}, AsMacroCall, FunctionId, TraitId, VariantId, }; @@ -155,6 +157,28 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { self.imp.ancestors_at_offset_with_macros(node, offset) } + /// Iterates all `ModuleDef`s and `Impl` blocks of the given file. + pub fn visit_file_defs(&self, file_id: FileId, cb: &mut dyn FnMut(Either)) { + let module = match self.to_module_def(file_id) { + Some(it) => it, + None => return, + }; + let mut defs: VecDeque<_> = module.declarations(self.db).into(); + while let Some(def) = defs.pop_front() { + if let ModuleDef::Module(submodule) = def { + if let ModuleSource::Module(_) = submodule.definition_source(self.db).value { + defs.extend(submodule.declarations(self.db)); + submodule + .impl_defs(self.db) + .into_iter() + .for_each(|impl_| cb(Either::Right(impl_))); + } + } + cb(Either::Left(def)); + } + module.impl_defs(self.db).into_iter().for_each(|impl_| cb(Either::Right(impl_))); + } + /// Find a AstNode by offset inside SyntaxNode, if it is inside *Macrofile*, /// search up until it is of the target AstNode type pub fn find_node_at_offset_with_macros( diff --git a/crates/ide/src/annotations.rs b/crates/ide/src/annotations.rs index 8e0a8fd8d..c3422ce70 100644 --- a/crates/ide/src/annotations.rs +++ b/crates/ide/src/annotations.rs @@ -1,17 +1,17 @@ -use hir::Semantics; +use either::Either; +use hir::{HasSource, Semantics}; use ide_db::{ - base_db::{FileId, FilePosition, FileRange, SourceDatabase}, - RootDatabase, SymbolKind, + base_db::{FileId, FilePosition, FileRange}, + RootDatabase, }; -use syntax::TextRange; +use syntax::{ast::NameOwner, AstNode, TextRange, TextSize}; use crate::{ - file_structure::file_structure, fn_references::find_all_methods, goto_implementation::goto_implementation, references::find_all_refs, runnables::{runnables, Runnable}, - NavigationTarget, RunnableKind, StructureNodeKind, + NavigationTarget, RunnableKind, }; // Feature: Annotations @@ -75,41 +75,56 @@ pub(crate) fn annotations( } } - file_structure(&db.parse(file_id).tree()) - .into_iter() - .filter(|node| { - matches!( - node.kind, - StructureNodeKind::SymbolKind(SymbolKind::Trait) - | StructureNodeKind::SymbolKind(SymbolKind::Struct) - | StructureNodeKind::SymbolKind(SymbolKind::Enum) - | StructureNodeKind::SymbolKind(SymbolKind::Union) - | StructureNodeKind::SymbolKind(SymbolKind::Const) - ) - }) - .for_each(|node| { - if config.annotate_impls - && node.kind != StructureNodeKind::SymbolKind(SymbolKind::Const) - { + Semantics::new(db).visit_file_defs(file_id, &mut |def| match def { + Either::Left(def) => { + let node = match def { + hir::ModuleDef::Const(konst) => { + konst.source(db).and_then(|node| range_and_position_of(&node.value)) + } + hir::ModuleDef::Trait(trait_) => { + trait_.source(db).and_then(|node| range_and_position_of(&node.value)) + } + hir::ModuleDef::Adt(hir::Adt::Struct(strukt)) => { + strukt.source(db).and_then(|node| range_and_position_of(&node.value)) + } + hir::ModuleDef::Adt(hir::Adt::Enum(enum_)) => { + enum_.source(db).and_then(|node| range_and_position_of(&node.value)) + } + hir::ModuleDef::Adt(hir::Adt::Union(union)) => { + union.source(db).and_then(|node| range_and_position_of(&node.value)) + } + _ => None, + }; + let (offset, range) = match node { + Some(node) => node, + None => return, + }; + + if config.annotate_impls && !matches!(def, hir::ModuleDef::Const(_)) { annotations.push(Annotation { - range: node.node_range, + range, kind: AnnotationKind::HasImpls { - position: FilePosition { file_id, offset: node.navigation_range.start() }, + position: FilePosition { file_id, offset }, data: None, }, }); } - if config.annotate_references { annotations.push(Annotation { - range: node.node_range, + range, kind: AnnotationKind::HasReferences { - position: FilePosition { file_id, offset: node.navigation_range.start() }, + position: FilePosition { file_id, offset }, data: None, }, }); } - }); + + fn range_and_position_of(node: &dyn NameOwner) -> Option<(TextSize, TextRange)> { + Some((node.name()?.syntax().text_range().start(), node.syntax().text_range())) + } + } + Either::Right(_) => (), + }); if config.annotate_method_references { annotations.extend(find_all_methods(db, file_id).into_iter().map(|method| Annotation { @@ -936,4 +951,19 @@ mod tests { "#]], ); } + + #[test] + fn test_no_annotations_outside_module_tree() { + check( + r#" +//- /foo.rs +struct Foo; +//- /lib.rs +// this file comes last since `check` checks the first file only +"#, + expect![[r#" + [] + "#]], + ); + } } diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs index 27d35de5b..17454f270 100644 --- a/crates/ide/src/runnables.rs +++ b/crates/ide/src/runnables.rs @@ -2,6 +2,7 @@ use std::fmt; use ast::NameOwner; use cfg::CfgExpr; +use either::Either; use hir::{AsAssocItem, HasAttrs, HasSource, HirDisplay, Semantics}; use ide_assists::utils::test_related_attribute; use ide_db::{ @@ -102,13 +103,27 @@ impl Runnable { // |=== pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec { let sema = Semantics::new(db); - let module = match sema.to_module_def(file_id) { - None => return Vec::new(), - Some(it) => it, - }; let mut res = Vec::new(); - runnables_mod(&sema, &mut res, module); + sema.visit_file_defs(file_id, &mut |def| match def { + Either::Left(def) => { + let runnable = match def { + hir::ModuleDef::Module(it) => runnable_mod(&sema, it), + hir::ModuleDef::Function(it) => runnable_fn(&sema, it), + _ => None, + }; + res.extend(runnable.or_else(|| module_def_doctest(&sema, def))) + } + Either::Right(impl_) => { + res.extend(impl_.items(db).into_iter().filter_map(|assoc| match assoc { + hir::AssocItem::Function(it) => { + runnable_fn(&sema, it).or_else(|| module_def_doctest(&sema, it.into())) + } + hir::AssocItem::Const(it) => module_def_doctest(&sema, it.into()), + hir::AssocItem::TypeAlias(it) => module_def_doctest(&sema, it.into()), + })) + } + }); res } @@ -211,39 +226,6 @@ fn parent_test_module(sema: &Semantics, fn_def: &ast::Fn) -> Optio }) } -fn runnables_mod(sema: &Semantics, acc: &mut Vec, module: hir::Module) { - acc.extend(module.declarations(sema.db).into_iter().filter_map(|def| { - let runnable = match def { - hir::ModuleDef::Module(it) => runnable_mod(&sema, it), - hir::ModuleDef::Function(it) => runnable_fn(&sema, it), - _ => None, - }; - runnable.or_else(|| module_def_doctest(&sema, def)) - })); - - acc.extend(module.impl_defs(sema.db).into_iter().flat_map(|it| it.items(sema.db)).filter_map( - |def| match def { - hir::AssocItem::Function(it) => { - runnable_fn(&sema, it).or_else(|| module_def_doctest(&sema, it.into())) - } - hir::AssocItem::Const(it) => module_def_doctest(&sema, it.into()), - hir::AssocItem::TypeAlias(it) => module_def_doctest(&sema, it.into()), - }, - )); - - for def in module.declarations(sema.db) { - if let hir::ModuleDef::Module(submodule) = def { - match submodule.definition_source(sema.db).value { - hir::ModuleSource::Module(_) => runnables_mod(sema, acc, submodule), - hir::ModuleSource::SourceFile(_) => { - cov_mark::hit!(dont_recurse_in_outline_submodules) - } - hir::ModuleSource::BlockExpr(_) => {} // inner items aren't runnable - } - } - } -} - pub(crate) fn runnable_fn(sema: &Semantics, def: hir::Function) -> Option { let func = def.source(sema.db)?; let name_string = def.name(sema.db).to_string(); @@ -1178,7 +1160,6 @@ mod tests { #[test] fn dont_recurse_in_outline_submodules() { - cov_mark::check!(dont_recurse_in_outline_submodules); check( r#" //- /lib.rs -- cgit v1.2.3 From 41745f48d5f867ff0896ce7906b5b4c04e72a767 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 15 Mar 2021 12:18:52 +0100 Subject: move Semantics::visit_file_defs to ide_db::helpers --- crates/hir/src/semantics.rs | 26 +------------------------- crates/ide/src/annotations.rs | 3 ++- crates/ide/src/runnables.rs | 3 ++- crates/ide_db/src/helpers.rs | 28 ++++++++++++++++++++++++++++ 4 files changed, 33 insertions(+), 27 deletions(-) diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index 2a0a36de4..945638cc5 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -2,12 +2,10 @@ mod source_to_def; -use std::{cell::RefCell, collections::VecDeque, fmt, iter::successors}; +use std::{cell::RefCell, fmt, iter::successors}; use base_db::{FileId, FileRange}; -use either::Either; use hir_def::{ - nameres::ModuleSource, resolver::{self, HasResolver, Resolver, TypeNs}, AsMacroCall, FunctionId, TraitId, VariantId, }; @@ -157,28 +155,6 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { self.imp.ancestors_at_offset_with_macros(node, offset) } - /// Iterates all `ModuleDef`s and `Impl` blocks of the given file. - pub fn visit_file_defs(&self, file_id: FileId, cb: &mut dyn FnMut(Either)) { - let module = match self.to_module_def(file_id) { - Some(it) => it, - None => return, - }; - let mut defs: VecDeque<_> = module.declarations(self.db).into(); - while let Some(def) = defs.pop_front() { - if let ModuleDef::Module(submodule) = def { - if let ModuleSource::Module(_) = submodule.definition_source(self.db).value { - defs.extend(submodule.declarations(self.db)); - submodule - .impl_defs(self.db) - .into_iter() - .for_each(|impl_| cb(Either::Right(impl_))); - } - } - cb(Either::Left(def)); - } - module.impl_defs(self.db).into_iter().for_each(|impl_| cb(Either::Right(impl_))); - } - /// Find a AstNode by offset inside SyntaxNode, if it is inside *Macrofile*, /// search up until it is of the target AstNode type pub fn find_node_at_offset_with_macros( diff --git a/crates/ide/src/annotations.rs b/crates/ide/src/annotations.rs index c3422ce70..72492f826 100644 --- a/crates/ide/src/annotations.rs +++ b/crates/ide/src/annotations.rs @@ -2,6 +2,7 @@ use either::Either; use hir::{HasSource, Semantics}; use ide_db::{ base_db::{FileId, FilePosition, FileRange}, + helpers::visit_file_defs, RootDatabase, }; use syntax::{ast::NameOwner, AstNode, TextRange, TextSize}; @@ -75,7 +76,7 @@ pub(crate) fn annotations( } } - Semantics::new(db).visit_file_defs(file_id, &mut |def| match def { + visit_file_defs(&Semantics::new(db), file_id, &mut |def| match def { Either::Left(def) => { let node = match def { hir::ModuleDef::Const(konst) => { diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs index 17454f270..0c7a8fbf8 100644 --- a/crates/ide/src/runnables.rs +++ b/crates/ide/src/runnables.rs @@ -8,6 +8,7 @@ use ide_assists::utils::test_related_attribute; use ide_db::{ base_db::{FilePosition, FileRange}, defs::Definition, + helpers::visit_file_defs, search::SearchScope, RootDatabase, SymbolKind, }; @@ -105,7 +106,7 @@ pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec { let sema = Semantics::new(db); let mut res = Vec::new(); - sema.visit_file_defs(file_id, &mut |def| match def { + visit_file_defs(&sema, file_id, &mut |def| match def { Either::Left(def) => { let runnable = match def { hir::ModuleDef::Module(it) => runnable_mod(&sema, it), diff --git a/crates/ide_db/src/helpers.rs b/crates/ide_db/src/helpers.rs index 3c95d3cff..9992a92bd 100644 --- a/crates/ide_db/src/helpers.rs +++ b/crates/ide_db/src/helpers.rs @@ -2,6 +2,10 @@ pub mod insert_use; pub mod import_assets; +use std::collections::VecDeque; + +use base_db::FileId; +use either::Either; use hir::{Crate, Enum, ItemInNs, MacroDef, Module, ModuleDef, Name, ScopeDef, Semantics, Trait}; use syntax::ast::{self, make}; @@ -39,6 +43,30 @@ pub fn mod_path_to_ast(path: &hir::ModPath) -> ast::Path { make::path_from_segments(segments, is_abs) } +/// Iterates all `ModuleDef`s and `Impl` blocks of the given file. +pub fn visit_file_defs( + sema: &Semantics, + file_id: FileId, + cb: &mut dyn FnMut(Either), +) { + let db = sema.db; + let module = match sema.to_module_def(file_id) { + Some(it) => it, + None => return, + }; + let mut defs: VecDeque<_> = module.declarations(db).into(); + while let Some(def) = defs.pop_front() { + if let ModuleDef::Module(submodule) = def { + if let hir::ModuleSource::Module(_) = submodule.definition_source(db).value { + defs.extend(submodule.declarations(db)); + submodule.impl_defs(db).into_iter().for_each(|impl_| cb(Either::Right(impl_))); + } + } + cb(Either::Left(def)); + } + module.impl_defs(db).into_iter().for_each(|impl_| cb(Either::Right(impl_))); +} + /// Helps with finding well-know things inside the standard library. This is /// somewhat similar to the known paths infra inside hir, but it different; We /// want to make sure that IDE specific paths don't become interesting inside -- cgit v1.2.3