diff options
Diffstat (limited to 'src/tools/rust-analyzer/crates/ide-completion')
20 files changed, 740 insertions, 143 deletions
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs index 480cb77b4..f60ac1501 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs @@ -20,6 +20,7 @@ pub(crate) mod r#type; pub(crate) mod use_; pub(crate) mod vis; pub(crate) mod env_vars; +pub(crate) mod extern_crate; use std::iter; @@ -703,7 +704,9 @@ pub(super) fn complete_name_ref( TypeLocation::TypeAscription(ascription) => { r#type::complete_ascribed_type(acc, ctx, path_ctx, ascription); } - TypeLocation::GenericArgList(_) + TypeLocation::GenericArg { .. } + | TypeLocation::AssocConstEq + | TypeLocation::AssocTypeEq | TypeLocation::TypeBound | TypeLocation::ImplTarget | TypeLocation::ImplTrait @@ -737,6 +740,7 @@ pub(super) fn complete_name_ref( } } } + NameRefKind::ExternCrate => extern_crate::complete_extern_crate(acc, ctx), NameRefKind::DotAccess(dot_access) => { flyimport::import_on_the_fly_dot(acc, ctx, dot_access); dot::complete_dot(acc, ctx, dot_access); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs index 62bdb6ee6..87a286778 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs @@ -1,10 +1,8 @@ //! Completion for cfg -use std::iter; - use ide_db::SymbolKind; use itertools::Itertools; -use syntax::SyntaxKind; +use syntax::{algo, ast::Ident, AstToken, Direction, NodeOrToken, SyntaxKind}; use crate::{completions::Completions, context::CompletionContext, CompletionItem}; @@ -15,31 +13,44 @@ pub(crate) fn complete_cfg(acc: &mut Completions, ctx: &CompletionContext<'_>) { acc.add(completion.build(ctx.db)); }; - let previous = iter::successors(ctx.original_token.prev_token(), |t| { - (matches!(t.kind(), SyntaxKind::EQ) || t.kind().is_trivia()) - .then(|| t.prev_token()) - .flatten() - }) - .find(|t| matches!(t.kind(), SyntaxKind::IDENT)); - - match previous.as_ref().map(|p| p.text()) { - Some("target_arch") => KNOWN_ARCH.iter().copied().for_each(add_completion), - Some("target_env") => KNOWN_ENV.iter().copied().for_each(add_completion), - Some("target_os") => KNOWN_OS.iter().copied().for_each(add_completion), - Some("target_vendor") => KNOWN_VENDOR.iter().copied().for_each(add_completion), - Some("target_endian") => ["little", "big"].into_iter().for_each(add_completion), - Some(name) => ctx.krate.potential_cfg(ctx.db).get_cfg_values(name).cloned().for_each(|s| { - let insert_text = format!(r#""{s}""#); - let mut item = CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), s); - item.insert_text(insert_text); + // FIXME: Move this into context/analysis.rs + let previous = ctx + .original_token + .prev_token() + .and_then(|it| { + if matches!(it.kind(), SyntaxKind::EQ) { + Some(it.into()) + } else { + algo::non_trivia_sibling(it.into(), Direction::Prev) + } + }) + .filter(|t| matches!(t.kind(), SyntaxKind::EQ)) + .and_then(|it| algo::non_trivia_sibling(it.prev_sibling_or_token()?, Direction::Prev)) + .map(|it| match it { + NodeOrToken::Node(_) => None, + NodeOrToken::Token(t) => Ident::cast(t), + }); + match previous { + Some(None) => (), + Some(Some(p)) => match p.text() { + "target_arch" => KNOWN_ARCH.iter().copied().for_each(add_completion), + "target_env" => KNOWN_ENV.iter().copied().for_each(add_completion), + "target_os" => KNOWN_OS.iter().copied().for_each(add_completion), + "target_vendor" => KNOWN_VENDOR.iter().copied().for_each(add_completion), + "target_endian" => ["little", "big"].into_iter().for_each(add_completion), + name => ctx.krate.potential_cfg(ctx.db).get_cfg_values(name).cloned().for_each(|s| { + let insert_text = format!(r#""{s}""#); + let mut item = CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), s); + item.insert_text(insert_text); - acc.add(item.build(ctx.db)); - }), + acc.add(item.build(ctx.db)); + }), + }, None => ctx.krate.potential_cfg(ctx.db).get_cfg_keys().cloned().unique().for_each(|s| { let item = CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), s); acc.add(item.build(ctx.db)); }), - }; + } } const KNOWN_ARCH: [&str; 20] = [ diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs index 9447bc7db..90dac1902 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs @@ -1,6 +1,6 @@ //! Completion for derives -use hir::{HasAttrs, ScopeDef}; -use ide_db::SymbolKind; +use hir::ScopeDef; +use ide_db::{documentation::HasDocs, SymbolKind}; use itertools::Itertools; use syntax::SmolStr; diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/lint.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/lint.rs index 6bc6f34ed..f9dec5380 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/lint.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/lint.rs @@ -1,5 +1,5 @@ //! Completion for lints -use ide_db::{generated::lints::Lint, SymbolKind}; +use ide_db::{documentation::Documentation, generated::lints::Lint, SymbolKind}; use syntax::ast; use crate::{context::CompletionContext, item::CompletionItem, Completions}; @@ -55,7 +55,7 @@ pub(super) fn complete_lint( _ => name.to_owned(), }; let mut item = CompletionItem::new(SymbolKind::Attribute, ctx.source_range(), label); - item.documentation(hir::Documentation::new(description.to_owned())); + item.documentation(Documentation::new(description.to_owned())); item.add_to(acc, ctx.db) } } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_crate.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_crate.rs new file mode 100644 index 000000000..f9cde4466 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_crate.rs @@ -0,0 +1,71 @@ +//! Completion for extern crates + +use hir::Name; +use ide_db::{documentation::HasDocs, SymbolKind}; + +use crate::{context::CompletionContext, CompletionItem, CompletionItemKind}; + +use super::Completions; + +pub(crate) fn complete_extern_crate(acc: &mut Completions, ctx: &CompletionContext<'_>) { + let imported_extern_crates: Vec<Name> = ctx.scope.extern_crate_decls().collect(); + + for (name, module) in ctx.scope.extern_crates() { + if imported_extern_crates.contains(&name) { + continue; + } + + let mut item = CompletionItem::new( + CompletionItemKind::SymbolKind(SymbolKind::Module), + ctx.source_range(), + name.to_smol_str(), + ); + item.set_documentation(module.docs(ctx.db)); + + item.add_to(acc, ctx.db); + } +} + +#[cfg(test)] +mod test { + use crate::tests::completion_list_no_kw; + + #[test] + fn can_complete_extern_crate() { + let case = r#" +//- /lib.rs crate:other_crate_a +// nothing here +//- /other_crate_b.rs crate:other_crate_b +pub mod good_mod{} +//- /lib.rs crate:crate_c +// nothing here +//- /lib.rs crate:lib deps:other_crate_a,other_crate_b,crate_c extern-prelude:other_crate_a +extern crate oth$0 +mod other_mod {} +"#; + + let completion_list = completion_list_no_kw(case); + + assert_eq!("md other_crate_a\n".to_string(), completion_list); + } + + #[test] + fn will_not_complete_existing_import() { + let case = r#" +//- /lib.rs crate:other_crate_a +// nothing here +//- /lib.rs crate:crate_c +// nothing here +//- /lib.rs crate:other_crate_b +// +//- /lib.rs crate:lib deps:other_crate_a,other_crate_b,crate_c extern-prelude:other_crate_a,other_crate_b +extern crate other_crate_b; +extern crate oth$0 +mod other_mod {} +"#; + + let completion_list = completion_list_no_kw(case); + + assert_eq!("md other_crate_a\n".to_string(), completion_list); + } +} diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs index 8e904fd60..cecbe7539 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs @@ -51,9 +51,7 @@ mod tests { fn works_when_wrapped() { check( r#" -macro_rules! format_args { - ($lit:literal $(tt:tt)*) => { 0 }, -} +//- minicore: fmt macro_rules! print { ($($arg:tt)*) => (std::io::_print(format_args!($($arg)*))); } @@ -70,9 +68,7 @@ fn main() { fn no_completion_without_brace() { check( r#" -macro_rules! format_args { - ($lit:literal $(tt:tt)*) => { 0 }, -} +//- minicore: fmt fn main() { let foobar = 1; format_args!("f$0"); @@ -87,18 +83,13 @@ fn main() { check_edit( "foobar", r#" -macro_rules! format_args { - ($lit:literal $(tt:tt)*) => { 0 }, -} +//- minicore: fmt fn main() { let foobar = 1; format_args!("{f$0"); } "#, r#" -macro_rules! format_args { - ($lit:literal $(tt:tt)*) => { 0 }, -} fn main() { let foobar = 1; format_args!("{foobar"); @@ -108,18 +99,13 @@ fn main() { check_edit( "foobar", r#" -macro_rules! format_args { - ($lit:literal $(tt:tt)*) => { 0 }, -} +//- minicore: fmt fn main() { let foobar = 1; format_args!("{$0"); } "#, r#" -macro_rules! format_args { - ($lit:literal $(tt:tt)*) => { 0 }, -} fn main() { let foobar = 1; format_args!("{foobar"); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs index 269e40e6e..42dfbfc7d 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs @@ -33,8 +33,8 @@ use hir::{self, HasAttrs}; use ide_db::{ - path_transform::PathTransform, syntax_helpers::insert_whitespace_into_node, - traits::get_missing_assoc_items, SymbolKind, + documentation::HasDocs, path_transform::PathTransform, + syntax_helpers::insert_whitespace_into_node, traits::get_missing_assoc_items, SymbolKind, }; use syntax::{ ast::{self, edit_in_place::AttrsOwnerEdit, HasTypeBounds}, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs index 2ffe12337..fc21bba45 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs @@ -2,8 +2,12 @@ mod format_like; -use hir::{Documentation, HasAttrs}; -use ide_db::{imports::insert_use::ImportScope, ty_filter::TryEnum, SnippetCap}; +use ide_db::{ + documentation::{Documentation, HasDocs}, + imports::insert_use::ImportScope, + ty_filter::TryEnum, + SnippetCap, +}; use syntax::{ ast::{self, make, AstNode, AstToken}, SyntaxKind::{BLOCK_EXPR, EXPR_STMT, FOR_EXPR, IF_EXPR, LOOP_EXPR, STMT_LIST, WHILE_EXPR}, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/snippet.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/snippet.rs index e9831a5b2..3ff68b978 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/snippet.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/snippet.rs @@ -1,7 +1,6 @@ //! This file provides snippet completions, like `pd` => `eprintln!(...)`. -use hir::Documentation; -use ide_db::{imports::insert_use::ImportScope, SnippetCap}; +use ide_db::{documentation::Documentation, imports::insert_use::ImportScope, SnippetCap}; use crate::{ context::{ExprCtx, ItemListKind, PathCompletionCtx, Qualified}, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs index e47054756..a30fd13b1 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs @@ -1,7 +1,7 @@ //! Completion of names from the current scope in type position. use hir::{HirDisplay, ScopeDef}; -use syntax::{ast, AstNode, SyntaxKind}; +use syntax::{ast, AstNode}; use crate::{ context::{PathCompletionCtx, Qualified, TypeAscriptionTarget, TypeLocation}, @@ -20,16 +20,15 @@ pub(crate) fn complete_type_path( let scope_def_applicable = |def| { use hir::{GenericParam::*, ModuleDef::*}; match def { - ScopeDef::GenericParam(LifetimeParam(_)) | ScopeDef::Label(_) => false, + ScopeDef::GenericParam(LifetimeParam(_)) => location.complete_lifetimes(), + ScopeDef::Label(_) => false, // no values in type places ScopeDef::ModuleDef(Function(_) | Variant(_) | Static(_)) | ScopeDef::Local(_) => false, // unless its a constant in a generic arg list position ScopeDef::ModuleDef(Const(_)) | ScopeDef::GenericParam(ConstParam(_)) => { - matches!(location, TypeLocation::GenericArgList(_)) - } - ScopeDef::ImplSelfType(_) => { - !matches!(location, TypeLocation::ImplTarget | TypeLocation::ImplTrait) + location.complete_consts() } + ScopeDef::ImplSelfType(_) => location.complete_self_type(), // Don't suggest attribute macros and derives. ScopeDef::ModuleDef(Macro(mac)) => mac.is_fn_like(ctx.db), // Type things are fine @@ -38,12 +37,12 @@ pub(crate) fn complete_type_path( ) | ScopeDef::AdtSelfType(_) | ScopeDef::Unknown - | ScopeDef::GenericParam(TypeParam(_)) => true, + | ScopeDef::GenericParam(TypeParam(_)) => location.complete_types(), } }; let add_assoc_item = |acc: &mut Completions, item| match item { - hir::AssocItem::Const(ct) if matches!(location, TypeLocation::GenericArgList(_)) => { + hir::AssocItem::Const(ct) if matches!(location, TypeLocation::GenericArg { .. }) => { acc.add_const(ctx, ct) } hir::AssocItem::Function(_) | hir::AssocItem::Const(_) => (), @@ -157,56 +156,30 @@ pub(crate) fn complete_type_path( }); return; } - TypeLocation::GenericArgList(Some(arg_list)) => { - let in_assoc_type_arg = ctx - .original_token - .parent_ancestors() - .any(|node| node.kind() == SyntaxKind::ASSOC_TYPE_ARG); - - if !in_assoc_type_arg { - if let Some(path_seg) = - arg_list.syntax().parent().and_then(ast::PathSegment::cast) - { - if path_seg - .syntax() - .ancestors() - .find_map(ast::TypeBound::cast) - .is_some() - { - if let Some(hir::PathResolution::Def(hir::ModuleDef::Trait( - trait_, - ))) = ctx.sema.resolve_path(&path_seg.parent_path()) - { - let arg_idx = arg_list - .generic_args() - .filter(|arg| { - arg.syntax().text_range().end() - < ctx.original_token.text_range().start() - }) - .count(); - - let n_required_params = - trait_.type_or_const_param_count(ctx.sema.db, true); - if arg_idx >= n_required_params { - trait_ - .items_with_supertraits(ctx.sema.db) - .into_iter() - .for_each(|it| { - if let hir::AssocItem::TypeAlias(alias) = it { - cov_mark::hit!( - complete_assoc_type_in_generics_list - ); - acc.add_type_alias_with_eq(ctx, alias); - } - }); - - let n_params = - trait_.type_or_const_param_count(ctx.sema.db, false); - if arg_idx >= n_params { - return; // only show assoc types - } - } + TypeLocation::GenericArg { + args: Some(arg_list), of_trait: Some(trait_), .. + } => { + if arg_list.syntax().ancestors().find_map(ast::TypeBound::cast).is_some() { + let arg_idx = arg_list + .generic_args() + .filter(|arg| { + arg.syntax().text_range().end() + < ctx.original_token.text_range().start() + }) + .count(); + + let n_required_params = trait_.type_or_const_param_count(ctx.sema.db, true); + if arg_idx >= n_required_params { + trait_.items_with_supertraits(ctx.sema.db).into_iter().for_each(|it| { + if let hir::AssocItem::TypeAlias(alias) = it { + cov_mark::hit!(complete_assoc_type_in_generics_list); + acc.add_type_alias_with_eq(ctx, alias); } + }); + + let n_params = trait_.type_or_const_param_count(ctx.sema.db, false); + if arg_idx >= n_params { + return; // only show assoc types } } } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs index 3cb65b272..0da7ba6d0 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs @@ -155,13 +155,63 @@ pub(crate) struct ExprCtx { pub(crate) enum TypeLocation { TupleField, TypeAscription(TypeAscriptionTarget), - GenericArgList(Option<ast::GenericArgList>), + /// Generic argument position e.g. `Foo<$0>` + GenericArg { + /// The generic argument list containing the generic arg + args: Option<ast::GenericArgList>, + /// `Some(trait_)` if `trait_` is being instantiated with `args` + of_trait: Option<hir::Trait>, + /// The generic parameter being filled in by the generic arg + corresponding_param: Option<ast::GenericParam>, + }, + /// Associated type equality constraint e.g. `Foo<Bar = $0>` + AssocTypeEq, + /// Associated constant equality constraint e.g. `Foo<X = $0>` + AssocConstEq, TypeBound, ImplTarget, ImplTrait, Other, } +impl TypeLocation { + pub(crate) fn complete_lifetimes(&self) -> bool { + matches!( + self, + TypeLocation::GenericArg { + corresponding_param: Some(ast::GenericParam::LifetimeParam(_)), + .. + } + ) + } + + pub(crate) fn complete_consts(&self) -> bool { + match self { + TypeLocation::GenericArg { + corresponding_param: Some(ast::GenericParam::ConstParam(_)), + .. + } => true, + TypeLocation::AssocConstEq => true, + _ => false, + } + } + + pub(crate) fn complete_types(&self) -> bool { + match self { + TypeLocation::GenericArg { corresponding_param: Some(param), .. } => { + matches!(param, ast::GenericParam::TypeParam(_)) + } + TypeLocation::AssocConstEq => false, + TypeLocation::AssocTypeEq => true, + _ => true, + } + } + + pub(crate) fn complete_self_type(&self) -> bool { + self.complete_types() && !matches!(self, TypeLocation::ImplTarget | TypeLocation::ImplTrait) + } +} + #[derive(Clone, Debug, PartialEq, Eq)] pub(crate) enum TypeAscriptionTarget { Let(Option<ast::Pat>), @@ -301,6 +351,7 @@ pub(super) enum NameRefKind { expr: ast::RecordExpr, }, Pattern(PatternContext), + ExternCrate, } /// The identifier we are currently completing. diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs index 3ea506590..1e6b2f319 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs @@ -1,11 +1,11 @@ //! Module responsible for analyzing the code surrounding the cursor for completion. use std::iter; -use hir::{Semantics, Type, TypeInfo, Variant}; +use hir::{HasSource, Semantics, Type, TypeInfo, Variant}; use ide_db::{active_parameter::ActiveParameter, RootDatabase}; use syntax::{ algo::{find_node_at_offset, non_trivia_sibling}, - ast::{self, AttrKind, HasArgList, HasLoopBody, HasName, NameOrNameRef}, + ast::{self, AttrKind, HasArgList, HasGenericParams, HasLoopBody, HasName, NameOrNameRef}, match_ast, AstNode, AstToken, Direction, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, TextRange, TextSize, T, }; @@ -624,6 +624,10 @@ fn classify_name_ref( }); return Some(make_res(kind)); }, + ast::ExternCrate(_) => { + let kind = NameRefKind::ExternCrate; + return Some(make_res(kind)); + }, ast::MethodCallExpr(method) => { let receiver = find_opt_node_in_file(original_file, method.receiver()); let kind = NameRefKind::DotAccess(DotAccess { @@ -719,6 +723,136 @@ fn classify_name_ref( None }; + let generic_arg_location = |arg: ast::GenericArg| { + let mut override_location = None; + let location = find_opt_node_in_file_compensated( + sema, + original_file, + arg.syntax().parent().and_then(ast::GenericArgList::cast), + ) + .map(|args| { + let mut in_trait = None; + let param = (|| { + let parent = args.syntax().parent()?; + let params = match_ast! { + match parent { + ast::PathSegment(segment) => { + match sema.resolve_path(&segment.parent_path().top_path())? { + hir::PathResolution::Def(def) => match def { + hir::ModuleDef::Function(func) => { + func.source(sema.db)?.value.generic_param_list() + } + hir::ModuleDef::Adt(adt) => { + adt.source(sema.db)?.value.generic_param_list() + } + hir::ModuleDef::Variant(variant) => { + variant.parent_enum(sema.db).source(sema.db)?.value.generic_param_list() + } + hir::ModuleDef::Trait(trait_) => { + if let ast::GenericArg::AssocTypeArg(arg) = &arg { + let arg_name = arg.name_ref()?; + let arg_name = arg_name.text(); + for item in trait_.items_with_supertraits(sema.db) { + match item { + hir::AssocItem::TypeAlias(assoc_ty) => { + if assoc_ty.name(sema.db).as_str()? == arg_name { + override_location = Some(TypeLocation::AssocTypeEq); + return None; + } + }, + hir::AssocItem::Const(const_) => { + if const_.name(sema.db)?.as_str()? == arg_name { + override_location = Some(TypeLocation::AssocConstEq); + return None; + } + }, + _ => (), + } + } + return None; + } else { + in_trait = Some(trait_); + trait_.source(sema.db)?.value.generic_param_list() + } + } + hir::ModuleDef::TraitAlias(trait_) => { + trait_.source(sema.db)?.value.generic_param_list() + } + hir::ModuleDef::TypeAlias(ty_) => { + ty_.source(sema.db)?.value.generic_param_list() + } + _ => None, + }, + _ => None, + } + }, + ast::MethodCallExpr(call) => { + let func = sema.resolve_method_call(&call)?; + func.source(sema.db)?.value.generic_param_list() + }, + ast::AssocTypeArg(arg) => { + let trait_ = ast::PathSegment::cast(arg.syntax().parent()?.parent()?)?; + match sema.resolve_path(&trait_.parent_path().top_path())? { + hir::PathResolution::Def(def) => match def { + hir::ModuleDef::Trait(trait_) => { + let arg_name = arg.name_ref()?; + let arg_name = arg_name.text(); + let trait_items = trait_.items_with_supertraits(sema.db); + let assoc_ty = trait_items.iter().find_map(|item| match item { + hir::AssocItem::TypeAlias(assoc_ty) => { + (assoc_ty.name(sema.db).as_str()? == arg_name) + .then_some(assoc_ty) + }, + _ => None, + })?; + assoc_ty.source(sema.db)?.value.generic_param_list() + } + _ => None, + }, + _ => None, + } + }, + _ => None, + } + }?; + // Determine the index of the argument in the `GenericArgList` and match it with + // the corresponding parameter in the `GenericParamList`. Since lifetime parameters + // are often omitted, ignore them for the purposes of matching the argument with + // its parameter unless a lifetime argument is provided explicitly. That is, for + // `struct S<'a, 'b, T>`, match `S::<$0>` to `T` and `S::<'a, $0, _>` to `'b`. + // FIXME: This operates on the syntax tree and will produce incorrect results when + // generic parameters are disabled by `#[cfg]` directives. It should operate on the + // HIR, but the functionality necessary to do so is not exposed at the moment. + let mut explicit_lifetime_arg = false; + let arg_idx = arg + .syntax() + .siblings(Direction::Prev) + // Skip the node itself + .skip(1) + .map(|arg| if ast::LifetimeArg::can_cast(arg.kind()) { explicit_lifetime_arg = true }) + .count(); + let param_idx = if explicit_lifetime_arg { + arg_idx + } else { + // Lifetimes parameters always precede type and generic parameters, + // so offset the argument index by the total number of lifetime params + arg_idx + params.lifetime_params().count() + }; + params.generic_params().nth(param_idx) + })(); + (args, in_trait, param) + }); + let (arg_list, of_trait, corresponding_param) = match location { + Some((arg_list, of_trait, param)) => (Some(arg_list), of_trait, param), + _ => (None, None, None), + }; + override_location.unwrap_or(TypeLocation::GenericArg { + args: arg_list, + of_trait, + corresponding_param, + }) + }; + let type_location = |node: &SyntaxNode| { let parent = node.parent()?; let res = match_ast! { @@ -774,9 +908,12 @@ fn classify_name_ref( ast::TypeBound(_) => TypeLocation::TypeBound, // is this case needed? ast::TypeBoundList(_) => TypeLocation::TypeBound, - ast::GenericArg(it) => TypeLocation::GenericArgList(find_opt_node_in_file_compensated(sema, original_file, it.syntax().parent().and_then(ast::GenericArgList::cast))), + ast::GenericArg(it) => generic_arg_location(it), // is this case needed? - ast::GenericArgList(it) => TypeLocation::GenericArgList(find_opt_node_in_file_compensated(sema, original_file, Some(it))), + ast::GenericArgList(it) => { + let args = find_opt_node_in_file_compensated(sema, original_file, Some(it)); + TypeLocation::GenericArg { args, of_trait: None, corresponding_param: None } + }, ast::TupleField(_) => TypeLocation::TupleField, _ => return None, } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs index 0309952c2..c45cc8d7b 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs @@ -2,8 +2,11 @@ use std::fmt; -use hir::{Documentation, Mutability}; -use ide_db::{imports::import_assets::LocatedImport, RootDatabase, SnippetCap, SymbolKind}; +use hir::Mutability; +use ide_db::{ + documentation::Documentation, imports::import_assets::LocatedImport, RootDatabase, SnippetCap, + SymbolKind, +}; use itertools::Itertools; use smallvec::SmallVec; use stdx::{impl_from, never}; diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index 1953eb479..dfe8fe7e2 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -12,7 +12,10 @@ pub(crate) mod literal; use hir::{AsAssocItem, HasAttrs, HirDisplay, ScopeDef}; use ide_db::{ - helpers::item_name, imports::import_assets::LocatedImport, RootDatabase, SnippetCap, SymbolKind, + documentation::{Documentation, HasDocs}, + helpers::item_name, + imports::import_assets::LocatedImport, + RootDatabase, SnippetCap, SymbolKind, }; use syntax::{AstNode, SmolStr, SyntaxKind, TextRange}; @@ -114,7 +117,7 @@ impl<'a> RenderContext<'a> { } // FIXME: remove this - fn docs(&self, def: impl HasAttrs) -> Option<hir::Documentation> { + fn docs(&self, def: impl HasDocs) -> Option<Documentation> { def.docs(self.db()) } } @@ -409,7 +412,7 @@ fn res_to_kind(resolution: ScopeDef) -> CompletionItemKind { } } -fn scope_def_docs(db: &RootDatabase, resolution: ScopeDef) -> Option<hir::Documentation> { +fn scope_def_docs(db: &RootDatabase, resolution: ScopeDef) -> Option<Documentation> { use hir::ModuleDef::*; match resolution { ScopeDef::ModuleDef(Module(it)) => it.docs(db), diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs index 728d236df..b218502f7 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs @@ -1,7 +1,10 @@ //! Renderer for `enum` variants. -use hir::{db::HirDatabase, Documentation, HasAttrs, StructKind}; -use ide_db::SymbolKind; +use hir::{db::HirDatabase, StructKind}; +use ide_db::{ + documentation::{Documentation, HasDocs}, + SymbolKind, +}; use crate::{ context::{CompletionContext, PathCompletionCtx, PathKind}, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs index ce7af1d34..68d175c2b 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs @@ -1,7 +1,7 @@ //! Renderer for macro invocations. -use hir::{Documentation, HirDisplay}; -use ide_db::SymbolKind; +use hir::HirDisplay; +use ide_db::{documentation::Documentation, SymbolKind}; use syntax::SmolStr; use crate::{ diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs index d06abc5e9..6f998119b 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs @@ -1,7 +1,7 @@ //! Renderer for patterns. -use hir::{db::HirDatabase, HasAttrs, Name, StructKind}; -use ide_db::SnippetCap; +use hir::{db::HirDatabase, Name, StructKind}; +use ide_db::{documentation::HasDocs, SnippetCap}; use itertools::Itertools; use syntax::SmolStr; @@ -103,7 +103,7 @@ fn build_completion( label: SmolStr, lookup: SmolStr, pat: String, - def: impl HasAttrs + Copy, + def: impl HasDocs + Copy, adt_ty: hir::Type, // Missing in context of match statement completions is_variant_missing: bool, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs index 1aaf39587..d8c134c53 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs @@ -67,11 +67,6 @@ struct Foo; } #[test] -fn inside_nested_attr() { - check(r#"#[cfg($0)]"#, expect![[]]) -} - -#[test] fn with_existing_attr() { check( r#"#[no_mangle] #[$0] mcall!();"#, @@ -636,6 +631,32 @@ mod cfg { use super::*; #[test] + fn inside_cfg() { + check( + r#" +//- /main.rs cfg:test,dbg=false,opt_level=2 +#[cfg($0)] +"#, + expect![[r#" + ba dbg + ba opt_level + ba test + "#]], + ); + check( + r#" +//- /main.rs cfg:test,dbg=false,opt_level=2 +#[cfg(b$0)] +"#, + expect![[r#" + ba dbg + ba opt_level + ba test + "#]], + ); + } + + #[test] fn cfg_target_endian() { check( r#"#[cfg(target_endian = $0"#, @@ -644,6 +665,13 @@ mod cfg { ba little "#]], ); + check( + r#"#[cfg(target_endian = b$0"#, + expect![[r#" + ba big + ba little + "#]], + ); } } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs index 8c038c0fb..4cdfd546f 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs @@ -1286,3 +1286,57 @@ macro_rules! println { expect![""], ) } + +#[test] +fn no_completions_for_external_doc_hidden_in_path() { + check( + r#" +//- /main.rs crate:main deps:dep +fn main() { + Span$0 +} +//- /lib.rs crate:dep +#[doc(hidden)] +pub mod bridge { + pub mod server { + pub trait Span + } +} +pub mod bridge2 { + #[doc(hidden)] + pub mod server2 { + pub trait Span + } +} +"#, + expect![""], + ); + // unless re-exported + check( + r#" +//- /main.rs crate:main deps:dep +fn main() { + Span$0 +} +//- /lib.rs crate:dep +#[doc(hidden)] +pub mod bridge { + pub mod server { + pub trait Span + } +} +pub use bridge::server::Span; +pub mod bridge2 { + #[doc(hidden)] + pub mod server2 { + pub trait Span2 + } +} +pub use bridge2::server2::Span2; +"#, + expect![[r#" + tt Span (use dep::Span) + tt Span2 (use dep::Span2) + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs index 8cb1ff4a1..d518dd764 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs @@ -384,10 +384,8 @@ trait Trait2<T>: Trait1 { fn foo<'lt, T: Trait2<$0>, const CONST_PARAM: usize>(_: T) {} "#, expect![[r#" - ct CONST - cp CONST_PARAM en Enum - ma makro!(…) macro_rules! makro + ma makro!(…) macro_rules! makro md module st Record st Tuple @@ -404,14 +402,13 @@ fn foo<'lt, T: Trait2<$0>, const CONST_PARAM: usize>(_: T) {} ); check( r#" -trait Trait2 { +trait Trait2<T> { type Foo; } fn foo<'lt, T: Trait2<self::$0>, const CONST_PARAM: usize>(_: T) {} "#, expect![[r#" - ct CONST en Enum ma makro!(…) macro_rules! makro md module @@ -437,7 +434,6 @@ trait Tr<T> { impl Tr<$0 "#, expect![[r#" - ct CONST en Enum ma makro!(…) macro_rules! makro md module @@ -485,7 +481,6 @@ trait MyTrait<T, U> { fn f(t: impl MyTrait<u$0 "#, expect![[r#" - ct CONST en Enum ma makro!(…) macro_rules! makro md module @@ -511,7 +506,6 @@ trait MyTrait<T, U> { fn f(t: impl MyTrait<u8, u$0 "#, expect![[r#" - ct CONST en Enum ma makro!(…) macro_rules! makro md module @@ -555,7 +549,6 @@ trait MyTrait<T, U = u8> { fn f(t: impl MyTrait<u$0 "#, expect![[r#" - ct CONST en Enum ma makro!(…) macro_rules! makro md module @@ -581,7 +574,6 @@ trait MyTrait<T, U = u8> { fn f(t: impl MyTrait<u8, u$0 "#, expect![[r#" - ct CONST en Enum ma makro!(…) macro_rules! makro md module @@ -627,7 +619,6 @@ trait MyTrait { fn f(t: impl MyTrait<Item1 = $0 "#, expect![[r#" - ct CONST en Enum ma makro!(…) macro_rules! makro md module @@ -653,7 +644,6 @@ trait MyTrait { fn f(t: impl MyTrait<Item1 = u8, Item2 = $0 "#, expect![[r#" - ct CONST en Enum ma makro!(…) macro_rules! makro md module @@ -668,6 +658,22 @@ fn f(t: impl MyTrait<Item1 = u8, Item2 = $0 kw self:: "#]], ); + + check( + r#" +trait MyTrait { + const C: usize; +}; + +fn f(t: impl MyTrait<C = $0 +"#, + expect![[r#" + ct CONST + ma makro!(…) macro_rules! makro + kw crate:: + kw self:: + "#]], + ); } #[test] @@ -719,3 +725,267 @@ pub struct S; "#]], ) } + +#[test] +fn completes_const_and_type_generics_separately() { + // Function generic params + check( + r#" + struct Foo; + const X: usize = 0; + fn foo<T, const N: usize>() {} + fn main() { + foo::<F$0, _>(); + } + "#, + expect![[r#" + en Enum + ma makro!(…) macro_rules! makro + md module + st Foo + st Record + st Tuple + st Unit + tt Trait + un Union + bt u32 + kw crate:: + kw self:: + "#]], + ); + // FIXME: This should probably also suggest completions for types, at least those that have + // associated constants usable in this position. For example, a user could be typing + // `foo::<_, { usize::MAX }>()`, but we currently don't suggest `usize` in constant position. + check( + r#" + struct Foo; + const X: usize = 0; + fn foo<T, const N: usize>() {} + fn main() { + foo::<_, $0>(); + } + "#, + expect![[r#" + ct CONST + ct X + ma makro!(…) macro_rules! makro + kw crate:: + kw self:: + "#]], + ); + + // Method generic params + check( + r#" + const X: usize = 0; + struct Foo; + impl Foo { fn bar<const N: usize, T>(self) {} } + fn main() { + Foo.bar::<_, $0>(); + } + "#, + expect![[r#" + en Enum + ma makro!(…) macro_rules! makro + md module + st Foo + st Record + st Tuple + st Unit + tt Trait + un Union + bt u32 + kw crate:: + kw self:: + "#]], + ); + check( + r#" + const X: usize = 0; + struct Foo; + impl Foo { fn bar<const N: usize, T>(self) {} } + fn main() { + Foo.bar::<X$0, _>(); + } + "#, + expect![[r#" + ct CONST + ct X + ma makro!(…) macro_rules! makro + kw crate:: + kw self:: + "#]], + ); + + // Associated type generic params + check( + r#" + const X: usize = 0; + struct Foo; + trait Bar { + type Baz<T, const X: usize>; + } + fn foo(_: impl Bar<Baz<F$0, 0> = ()>) {} + "#, + expect![[r#" + en Enum + ma makro!(…) macro_rules! makro + md module + st Foo + st Record + st Tuple + st Unit + tt Bar + tt Trait + un Union + bt u32 + kw crate:: + kw self:: + "#]], + ); + check( + r#" + const X: usize = 0; + struct Foo; + trait Bar { + type Baz<T, const X: usize>; + } + fn foo<T: Bar<Baz<(), $0> = ()>>() {} + "#, + expect![[r#" + ct CONST + ct X + ma makro!(…) macro_rules! makro + kw crate:: + kw self:: + "#]], + ); + + // Type generic params + check( + r#" + const X: usize = 0; + struct Foo<T, const N: usize>(T); + fn main() { + let _: Foo::<_, $0> = Foo(()); + } + "#, + expect![[r#" + ct CONST + ct X + ma makro!(…) macro_rules! makro + kw crate:: + kw self:: + "#]], + ); + + // Type alias generic params + check( + r#" + const X: usize = 0; + struct Foo<T, const N: usize>(T); + type Bar<const X: usize, U> = Foo<U, X>; + fn main() { + let _: Bar::<X$0, _> = Bar(()); + } + "#, + expect![[r#" + ct CONST + ct X + ma makro!(…) macro_rules! makro + kw crate:: + kw self:: + "#]], + ); + + // Enum variant params + check( + r#" + const X: usize = 0; + enum Foo<T, const N: usize> { A(T), B } + fn main() { + Foo::B::<(), $0>; + } + "#, + expect![[r#" + ct CONST + ct X + ma makro!(…) macro_rules! makro + kw crate:: + kw self:: + "#]], + ); + + // Trait params + check( + r#" + const X: usize = 0; + trait Foo<T, const N: usize> {} + impl Foo<(), $0> for () {} + "#, + expect![[r#" + ct CONST + ct X + ma makro!(…) macro_rules! makro + kw crate:: + kw self:: + "#]], + ); + + // Trait alias params + check( + r#" + #![feature(trait_alias)] + const X: usize = 0; + trait Foo<T, const N: usize> {} + trait Bar<const M: usize, U> = Foo<U, M>; + fn foo<T: Bar<X$0, ()>>() {} + "#, + expect![[r#" + ct CONST + ct X + ma makro!(…) macro_rules! makro + kw crate:: + kw self:: + "#]], + ); + + // Omitted lifetime params + check( + r#" +struct S<'a, 'b, const C: usize, T>(core::marker::PhantomData<&'a &'b T>); +fn foo<'a>() { S::<F$0, _>; } + "#, + expect![[r#" + ct CONST + ma makro!(…) macro_rules! makro + kw crate:: + kw self:: + "#]], + ); + // Explicit lifetime params + check( + r#" +struct S<'a, 'b, const C: usize, T>(core::marker::PhantomData<&'a &'b T>); +fn foo<'a>() { S::<'static, 'static, F$0, _>; } + "#, + expect![[r#" + ct CONST + ma makro!(…) macro_rules! makro + kw crate:: + kw self:: + "#]], + ); + check( + r#" +struct S<'a, 'b, const C: usize, T>(core::marker::PhantomData<&'a &'b T>); +fn foo<'a>() { S::<'static, F$0, _, _>; } + "#, + expect![[r#" + lt 'a + ma makro!(…) macro_rules! makro + kw crate:: + kw self:: + "#]], + ); +} |