summaryrefslogtreecommitdiffstats
path: root/src/tools/rust-analyzer/crates/ide-completion
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/rust-analyzer/crates/ide-completion')
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs57
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/lint.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_crate.rs71
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs22
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/snippet.rs3
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs87
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/context.rs53
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs145
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/item.rs7
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render.rs9
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs7
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs38
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs54
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs294
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::
+ "#]],
+ );
+}