Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide_db/src/helpers/insert_use.rs')
-rw-r--r--crates/ide_db/src/helpers/insert_use.rs61
1 files changed, 54 insertions, 7 deletions
diff --git a/crates/ide_db/src/helpers/insert_use.rs b/crates/ide_db/src/helpers/insert_use.rs
index 9889dd772e..1f3e3c55bf 100644
--- a/crates/ide_db/src/helpers/insert_use.rs
+++ b/crates/ide_db/src/helpers/insert_use.rs
@@ -8,7 +8,7 @@ use hir::Semantics;
use syntax::{
algo,
ast::{self, make, AstNode, HasAttrs, HasModuleItem, HasVisibility, PathSegmentKind},
- match_ast, ted, AstToken, Direction, NodeOrToken, SyntaxNode, SyntaxToken,
+ ted, AstToken, Direction, NodeOrToken, SyntaxNode, SyntaxToken,
};
use crate::{
@@ -50,7 +50,10 @@ pub enum ImportScope {
}
impl ImportScope {
+ // FIXME: Remove this?
+ #[cfg(test)]
fn from(syntax: SyntaxNode) -> Option<Self> {
+ use syntax::match_ast;
fn contains_cfg_attr(attrs: &dyn HasAttrs) -> bool {
attrs
.attrs()
@@ -76,16 +79,60 @@ impl ImportScope {
}
/// Determines the containing syntax node in which to insert a `use` statement affecting `position`.
- pub fn find_insert_use_container_with_macros(
+ /// Returns the original source node inside attributes.
+ pub fn find_insert_use_container(
position: &SyntaxNode,
sema: &Semantics<'_, RootDatabase>,
) -> Option<Self> {
- sema.ancestors_with_macros(position.clone()).find_map(Self::from)
- }
+ fn contains_cfg_attr(attrs: &dyn HasAttrs) -> bool {
+ attrs
+ .attrs()
+ .any(|attr| attr.as_simple_call().map_or(false, |(ident, _)| ident == "cfg"))
+ }
- /// Determines the containing syntax node in which to insert a `use` statement affecting `position`.
- pub fn find_insert_use_container(position: &SyntaxNode) -> Option<Self> {
- std::iter::successors(Some(position.clone()), SyntaxNode::parent).find_map(Self::from)
+ // Walk up the ancestor tree searching for a suitable node to do insertions on
+ // with special handling on cfg-gated items, in which case we want to insert imports locally
+ // or FIXME: annotate inserted imports with the same cfg
+ for syntax in sema.ancestors_with_macros(position.clone()) {
+ if let Some(file) = ast::SourceFile::cast(syntax.clone()) {
+ return Some(ImportScope::File(file));
+ } else if let Some(item) = ast::Item::cast(syntax) {
+ return match item {
+ ast::Item::Const(konst) if contains_cfg_attr(&konst) => {
+ // FIXME: Instead of bailing out with None, we should note down that
+ // this import needs an attribute added
+ match sema.original_ast_node(konst)?.body()? {
+ ast::Expr::BlockExpr(block) => block,
+ _ => return None,
+ }
+ .stmt_list()
+ .map(ImportScope::Block)
+ }
+ ast::Item::Fn(func) if contains_cfg_attr(&func) => {
+ // FIXME: Instead of bailing out with None, we should note down that
+ // this import needs an attribute added
+ sema.original_ast_node(func)?.body()?.stmt_list().map(ImportScope::Block)
+ }
+ ast::Item::Static(statik) if contains_cfg_attr(&statik) => {
+ // FIXME: Instead of bailing out with None, we should note down that
+ // this import needs an attribute added
+ match sema.original_ast_node(statik)?.body()? {
+ ast::Expr::BlockExpr(block) => block,
+ _ => return None,
+ }
+ .stmt_list()
+ .map(ImportScope::Block)
+ }
+ ast::Item::Module(module) => {
+ // early return is important here, if we can't find the original module
+ // in the input there is no way for us to insert an import anywhere.
+ sema.original_ast_node(module)?.item_list().map(ImportScope::Module)
+ }
+ _ => continue,
+ };
+ }
+ }
+ None
}
pub fn as_syntax_node(&self) -> &SyntaxNode {