11676: internal: Expand into pseudo-derive attribute expansions in completions r=Veykril a=Veykril

With this we now properly handle qualified path completions in derives
bors r+

Co-authored-by: Lukas Wirth <lukastw97@gmail.com>
This commit is contained in:
bors[bot] 2022-03-10 21:24:11 +00:00 committed by GitHub
commit 69e5bd5a25
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 486 additions and 392 deletions

View File

@ -18,8 +18,8 @@ use syntax::SmolStr;
use crate::{
Adt, Const, ConstParam, Enum, Field, Function, GenericParam, HasCrate, HasVisibility,
LifetimeParam, Module, Static, Struct, Trait, TyBuilder, Type, TypeAlias, TypeOrConstParam,
TypeParam, Union, Variant,
LifetimeParam, Macro, Module, Static, Struct, Trait, TyBuilder, Type, TypeAlias,
TypeOrConstParam, TypeParam, Union, Variant,
};
impl HirDisplay for Function {
@ -509,3 +509,14 @@ impl HirDisplay for Module {
}
}
}
impl HirDisplay for Macro {
fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
match self.id {
hir_def::MacroId::Macro2Id(_) => write!(f, "macro"),
hir_def::MacroId::MacroRulesId(_) => write!(f, "macro_rules!"),
hir_def::MacroId::ProcMacroId(_) => write!(f, "proc_macro"),
}?;
write!(f, " {}", self.name(f.db))
}
}

View File

@ -1811,6 +1811,10 @@ impl Macro {
pub fn is_attr(&self, db: &dyn HirDatabase) -> bool {
matches!(self.kind(db), MacroKind::Attr)
}
pub fn is_derive(&self, db: &dyn HirDatabase) -> bool {
matches!(self.kind(db), MacroKind::Derive)
}
}
impl HasVisibility for Macro {

View File

@ -151,6 +151,10 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
self.imp.expand_attr_macro(item)
}
pub fn expand_derive_as_pseudo_attr_macro(&self, attr: &ast::Attr) -> Option<SyntaxNode> {
self.imp.expand_derive_as_pseudo_attr_macro(attr)
}
pub fn resolve_derive_macro(&self, derive: &ast::Attr) -> Option<Vec<Option<Macro>>> {
self.imp.resolve_derive_macro(derive)
}
@ -185,6 +189,19 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
self.imp.speculative_expand_attr(actual_macro_call, speculative_args, token_to_map)
}
pub fn speculative_expand_derive_as_pseudo_attr_macro(
&self,
actual_macro_call: &ast::Attr,
speculative_args: &ast::Attr,
token_to_map: SyntaxToken,
) -> Option<(SyntaxNode, SyntaxToken)> {
self.imp.speculative_expand_derive_as_pseudo_attr_macro(
actual_macro_call,
speculative_args,
token_to_map,
)
}
/// Descend the token into macrocalls to its first mapped counterpart.
pub fn descend_into_macros_single(&self, token: SyntaxToken) -> SyntaxToken {
self.imp.descend_into_macros_single(token)
@ -438,9 +455,16 @@ impl<'db> SemanticsImpl<'db> {
fn expand_attr_macro(&self, item: &ast::Item) -> Option<SyntaxNode> {
let src = self.wrap_node_infile(item.clone());
let macro_call_id = self.with_ctx(|ctx| ctx.item_to_macro_call(src))?;
let file_id = macro_call_id.as_file();
let node = self.parse_or_expand(file_id)?;
Some(node)
self.parse_or_expand(macro_call_id.as_file())
}
fn expand_derive_as_pseudo_attr_macro(&self, attr: &ast::Attr) -> Option<SyntaxNode> {
let src = self.wrap_node_infile(attr.clone());
let adt = attr.syntax().parent().and_then(ast::Adt::cast)?;
let call_id = self.with_ctx(|ctx| {
ctx.attr_to_derive_macro_call(src.with_value(&adt), src).map(|(_, it, _)| it)
})?;
self.parse_or_expand(call_id.as_file())
}
fn resolve_derive_macro(&self, attr: &ast::Attr) -> Option<Vec<Option<Macro>>> {
@ -533,6 +557,25 @@ impl<'db> SemanticsImpl<'db> {
)
}
fn speculative_expand_derive_as_pseudo_attr_macro(
&self,
actual_macro_call: &ast::Attr,
speculative_args: &ast::Attr,
token_to_map: SyntaxToken,
) -> Option<(SyntaxNode, SyntaxToken)> {
let attr = self.wrap_node_infile(actual_macro_call.clone());
let adt = actual_macro_call.syntax().parent().and_then(ast::Adt::cast)?;
let macro_call_id = self.with_ctx(|ctx| {
ctx.attr_to_derive_macro_call(attr.with_value(&adt), attr).map(|(_, it, _)| it)
})?;
hir_expand::db::expand_speculative(
self.db.upcast(),
macro_call_id,
speculative_args.syntax(),
token_to_map,
)
}
// This might not be the correct way to do this, but it works for now
fn descend_node_into_attributes<N: AstNode>(&self, node: N) -> SmallVec<[N; 1]> {
let mut res = smallvec![];

View File

@ -1,7 +1,5 @@
//! Builtin attributes.
use itertools::Itertools;
use crate::{db::AstDatabase, name, ExpandResult, MacroCallId, MacroCallKind};
macro_rules! register_builtin {
@ -98,10 +96,16 @@ fn derive_attr_expand(
) -> ExpandResult<tt::Subtree> {
let loc = db.lookup_intern_macro_call(id);
let derives = match &loc.kind {
MacroCallKind::Attr { attr_args, .. } => &attr_args.0,
_ => return ExpandResult::ok(tt.clone()),
MacroCallKind::Attr { attr_args, is_derive: true, .. } => &attr_args.0,
_ => return ExpandResult::ok(Default::default()),
};
pseudo_derive_attr_expansion(tt, derives)
}
pub fn pseudo_derive_attr_expansion(
tt: &tt::Subtree,
args: &tt::Subtree,
) -> ExpandResult<tt::Subtree> {
let mk_leaf = |char| {
tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct {
char,
@ -111,21 +115,12 @@ fn derive_attr_expand(
};
let mut token_trees = Vec::new();
for (comma, group) in &derives
.token_trees
.iter()
.filter_map(|tt| match tt {
tt::TokenTree::Leaf(l) => Some(l),
tt::TokenTree::Subtree(_) => None,
})
.group_by(|l| matches!(l, tt::Leaf::Punct(tt::Punct { char: ',', .. })))
for tt in (&args.token_trees)
.split(|tt| matches!(tt, tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: ',', .. }))))
{
if comma {
continue;
}
token_trees.push(mk_leaf('#'));
token_trees.push(mk_leaf('['));
token_trees.extend(group.cloned().map(tt::TokenTree::Leaf));
token_trees.extend(tt.iter().cloned());
token_trees.push(mk_leaf(']'));
}
token_trees.push(mk_leaf('('));

View File

@ -14,10 +14,10 @@ use syntax::{
};
use crate::{
ast_id_map::AstIdMap, fixup, hygiene::HygieneFrame, BuiltinAttrExpander, BuiltinDeriveExpander,
BuiltinFnLikeExpander, ExpandError, ExpandResult, ExpandTo, HirFileId, HirFileIdRepr,
MacroCallId, MacroCallKind, MacroCallLoc, MacroDefId, MacroDefKind, MacroFile,
ProcMacroExpander,
ast_id_map::AstIdMap, builtin_attr_macro::pseudo_derive_attr_expansion, fixup,
hygiene::HygieneFrame, BuiltinAttrExpander, BuiltinDeriveExpander, BuiltinFnLikeExpander,
ExpandError, ExpandResult, ExpandTo, HirFileId, HirFileIdRepr, MacroCallId, MacroCallKind,
MacroCallLoc, MacroDefId, MacroDefKind, MacroFile, ProcMacroExpander,
};
/// Total limit on the number of tokens produced by any macro invocation.
@ -161,14 +161,16 @@ pub fn expand_speculative(
);
let (attr_arg, token_id) = match loc.kind {
MacroCallKind::Attr { invoc_attr_index, .. } => {
// Attributes may have an input token tree, build the subtree and map for this as well
// then try finding a token id for our token if it is inside this input subtree.
let item = ast::Item::cast(speculative_args.clone())?;
let attr = item
.doc_comments_and_attrs()
.nth(invoc_attr_index as usize)
.and_then(Either::left)?;
MacroCallKind::Attr { invoc_attr_index, is_derive, .. } => {
let attr = if is_derive {
// for pseudo-derive expansion we actually pass the attribute itself only
ast::Attr::cast(speculative_args.clone())
} else {
// Attributes may have an input token tree, build the subtree and map for this as well
// then try finding a token id for our token if it is inside this input subtree.
let item = ast::Item::cast(speculative_args.clone())?;
item.doc_comments_and_attrs().nth(invoc_attr_index as usize).and_then(Either::left)
}?;
match attr.token_tree() {
Some(token_tree) => {
let (mut tree, map) = syntax_node_to_token_tree(attr.token_tree()?.syntax());
@ -205,11 +207,15 @@ pub fn expand_speculative(
// Do the actual expansion, we need to directly expand the proc macro due to the attribute args
// Otherwise the expand query will fetch the non speculative attribute args and pass those instead.
let mut speculative_expansion = if let MacroDefKind::ProcMacro(expander, ..) = loc.def.kind {
tt.delimiter = None;
expander.expand(db, loc.krate, &tt, attr_arg.as_ref())
} else {
macro_def.expand(db, actual_macro_call, &tt)
let mut speculative_expansion = match loc.def.kind {
MacroDefKind::ProcMacro(expander, ..) => {
tt.delimiter = None;
expander.expand(db, loc.krate, &tt, attr_arg.as_ref())
}
MacroDefKind::BuiltInAttr(BuiltinAttrExpander::Derive, _) => {
pseudo_derive_attr_expansion(&tt, attr_arg.as_ref()?)
}
_ => macro_def.expand(db, actual_macro_call, &tt),
};
let expand_to = macro_expand_to(db, actual_macro_call);

View File

@ -2,7 +2,7 @@
use std::fmt::Display;
use either::Either;
use hir::{AsAssocItem, AttributeTemplate, HasAttrs, HasSource, HirDisplay, Semantics, TypeInfo};
use hir::{AsAssocItem, AttributeTemplate, HasAttrs, HirDisplay, Semantics, TypeInfo};
use ide_db::{
base_db::SourceDatabase,
defs::Definition,
@ -13,9 +13,7 @@ use ide_db::{
use itertools::Itertools;
use stdx::format_to;
use syntax::{
algo, ast,
display::{fn_as_proc_macro_label, macro_label},
match_ast, AstNode, Direction,
algo, ast, match_ast, AstNode, Direction,
SyntaxKind::{LET_EXPR, LET_STMT},
SyntaxToken, T,
};
@ -342,14 +340,8 @@ pub(super) fn definition(
) -> Option<Markup> {
let mod_path = definition_mod_path(db, &def);
let (label, docs) = match def {
Definition::Macro(it) => (
match &it.source(db)?.value {
Either::Left(mac) => macro_label(mac),
Either::Right(mac_fn) => fn_as_proc_macro_label(mac_fn),
},
it.attrs(db).docs(),
),
Definition::Field(def) => label_and_docs(db, def),
Definition::Macro(it) => label_and_docs(db, it),
Definition::Field(it) => label_and_docs(db, it),
Definition::Module(it) => label_and_docs(db, it),
Definition::Function(it) => label_and_docs(db, it),
Definition::Adt(it) => label_and_docs(db, it),

View File

@ -4102,16 +4102,16 @@ identity!{
}
"#,
expect![[r#"
*Copy*
*Copy*
```rust
test
```
```rust
test
```
```rust
pub macro Copy
```
"#]],
```rust
macro Copy
```
"#]],
);
}
@ -4126,16 +4126,16 @@ pub macro Copy {}
struct Foo;
"#,
expect![[r#"
*Copy*
*Copy*
```rust
test
```
```rust
test
```
```rust
pub macro Copy
```
"#]],
```rust
macro Copy
```
"#]],
);
check(
r#"
@ -4148,16 +4148,16 @@ mod foo {
struct Foo;
"#,
expect![[r#"
*Copy*
*Copy*
```rust
test::foo
```
```rust
test::foo
```
```rust
pub macro Copy
```
"#]],
```rust
macro Copy
```
"#]],
);
}

View File

@ -32,7 +32,7 @@ pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
}
let mac_input = tt.syntax().children_with_tokens().skip(1).take_while(|it| *it != r_delim);
let input_expressions = mac_input.into_iter().group_by(|tok| tok.kind() == T![,]);
let input_expressions = mac_input.group_by(|tok| tok.kind() == T![,]);
let input_expressions = input_expressions
.into_iter()
.filter_map(|(is_sep, group)| (!is_sep).then(|| group))

View File

@ -29,6 +29,8 @@ mod derive;
mod lint;
mod repr;
pub(crate) use self::derive::complete_derive;
/// Complete inputs to known builtin attributes as well as derive attributes
pub(crate) fn complete_known_attribute_input(
acc: &mut Completions,
@ -46,7 +48,6 @@ pub(crate) fn complete_known_attribute_input(
match path.text().as_str() {
"repr" => repr::complete_repr(acc, ctx, tt),
"derive" => derive::complete_derive(acc, ctx, ctx.attr.as_ref()?),
"feature" => lint::complete_lint(acc, ctx, &parse_tt_as_comma_sep_paths(tt)?, FEATURES),
"allow" | "warn" | "deny" | "forbid" => {
let existing_lints = parse_tt_as_comma_sep_paths(tt)?;
@ -62,9 +63,7 @@ pub(crate) fn complete_known_attribute_input(
lint::complete_lint(acc, ctx, &existing_lints, &lints);
}
"cfg" => {
cfg::complete_cfg(acc, ctx);
}
"cfg" => cfg::complete_cfg(acc, ctx),
_ => (),
}
Some(())
@ -347,7 +346,7 @@ fn parse_comma_sep_expr(input: ast::TokenTree) -> Option<Vec<ast::Expr>> {
.children_with_tokens()
.skip(1)
.take_while(|it| it.as_token() != Some(&r_paren));
let input_expressions = tokens.into_iter().group_by(|tok| tok.kind() == T![,]);
let input_expressions = tokens.group_by(|tok| tok.kind() == T![,]);
Some(
input_expressions
.into_iter()

View File

@ -1,123 +1,111 @@
//! Completion for derives
use hir::{HasAttrs, Macro, MacroKind};
use ide_db::{
imports::{import_assets::ImportAssets, insert_use::ImportScope},
SymbolKind,
};
use hir::{HasAttrs, ScopeDef};
use ide_db::SymbolKind;
use itertools::Itertools;
use rustc_hash::FxHashSet;
use syntax::{ast, SmolStr, SyntaxKind};
use syntax::SmolStr;
use crate::{
completions::flyimport::compute_fuzzy_completion_order_key, context::CompletionContext,
item::CompletionItem, Completions, ImportEdit,
context::{CompletionContext, PathCompletionCtx, PathKind, PathQualifierCtx},
item::CompletionItem,
Completions,
};
pub(super) fn complete_derive(acc: &mut Completions, ctx: &CompletionContext, attr: &ast::Attr) {
let core = ctx.famous_defs().core();
let existing_derives: FxHashSet<_> =
ctx.sema.resolve_derive_macro(attr).into_iter().flatten().flatten().collect();
for (name, mac) in get_derives_in_scope(ctx) {
if existing_derives.contains(&mac) {
continue;
}
let name = name.to_smol_str();
let (label, lookup) = match (core, mac.module(ctx.db).krate()) {
// show derive dependencies for `core`/`std` derives
(Some(core), mac_krate) if core == mac_krate => {
if let Some(derive_completion) = DEFAULT_DERIVE_DEPENDENCIES
.iter()
.find(|derive_completion| derive_completion.label == name)
{
let mut components = vec![derive_completion.label];
components.extend(derive_completion.dependencies.iter().filter(
|&&dependency| {
!existing_derives
.iter()
.map(|it| it.name(ctx.db))
.any(|it| it.to_smol_str() == dependency)
},
));
let lookup = components.join(", ");
let label = Itertools::intersperse(components.into_iter().rev(), ", ");
(SmolStr::from_iter(label), Some(lookup))
} else {
(name, None)
}
}
_ => (name, None),
};
let mut item = CompletionItem::new(SymbolKind::Derive, ctx.source_range(), label);
if let Some(docs) = mac.docs(ctx.db) {
item.documentation(docs);
}
if let Some(lookup) = lookup {
item.lookup_by(lookup);
}
item.add_to(acc);
}
flyimport_derive(acc, ctx);
}
fn get_derives_in_scope(ctx: &CompletionContext) -> Vec<(hir::Name, Macro)> {
let mut result = Vec::default();
ctx.process_all_names(&mut |name, scope_def| {
if let hir::ScopeDef::ModuleDef(hir::ModuleDef::Macro(mac)) = scope_def {
if mac.kind(ctx.db) == hir::MacroKind::Derive {
result.push((name, mac));
}
}
});
result
}
fn flyimport_derive(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
if ctx.token.kind() != SyntaxKind::IDENT {
return None;
pub(crate) fn complete_derive(acc: &mut Completions, ctx: &CompletionContext) {
let (qualifier, is_absolute_path) = match ctx.path_context {
Some(PathCompletionCtx {
kind: Some(PathKind::Derive),
ref qualifier,
is_absolute_path,
..
}) => (qualifier, is_absolute_path),
_ => return,
};
let potential_import_name = ctx.token.to_string();
let module = ctx.module?;
let parent = ctx.token.parent()?;
let user_input_lowercased = potential_import_name.to_lowercase();
let import_assets = ImportAssets::for_fuzzy_path(
module,
None,
potential_import_name,
&ctx.sema,
parent.clone(),
)?;
let import_scope = ImportScope::find_insert_use_container(&parent, &ctx.sema)?;
acc.add_all(
import_assets
.search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind)
.into_iter()
.filter_map(|import| match import.original_item {
hir::ItemInNs::Macros(mac) => Some((import, mac)),
_ => None,
})
.filter(|&(_, mac)| mac.kind(ctx.db) == MacroKind::Derive)
.filter(|&(_, mac)| !ctx.is_item_hidden(&hir::ItemInNs::Macros(mac)))
.sorted_by_key(|(import, _)| {
compute_fuzzy_completion_order_key(&import.import_path, &user_input_lowercased)
})
.filter_map(|(import, mac)| {
let mut item = CompletionItem::new(
SymbolKind::Derive,
ctx.source_range(),
mac.name(ctx.db).to_smol_str(),
);
item.add_import(ImportEdit { import, scope: import_scope.clone() });
if let Some(docs) = mac.docs(ctx.db) {
item.documentation(docs);
let core = ctx.famous_defs().core();
match qualifier {
Some(PathQualifierCtx { resolution, is_super_chain, .. }) => {
if *is_super_chain {
acc.add_keyword(ctx, "super::");
}
let module = match resolution {
Some(hir::PathResolution::Def(hir::ModuleDef::Module(it))) => it,
_ => return,
};
for (name, def) in module.scope(ctx.db, ctx.module) {
let add_def = match def {
ScopeDef::ModuleDef(hir::ModuleDef::Macro(mac)) => {
!ctx.existing_derives.contains(&mac) && mac.is_derive(ctx.db)
}
ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) => true,
_ => false,
};
if add_def {
acc.add_resolution(ctx, name, def);
}
Some(item.build())
}),
);
Some(())
}
return;
}
None if is_absolute_path => acc.add_crate_roots(ctx),
// only show modules in a fresh UseTree
None => {
ctx.process_all_names(&mut |name, def| {
let mac = match def {
ScopeDef::ModuleDef(hir::ModuleDef::Macro(mac))
if !ctx.existing_derives.contains(&mac) && mac.is_derive(ctx.db) =>
{
mac
}
ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) => {
return acc.add_resolution(ctx, name, def);
}
_ => return,
};
match (core, mac.module(ctx.db).krate()) {
// show derive dependencies for `core`/`std` derives
(Some(core), mac_krate) if core == mac_krate && qualifier.is_none() => {}
_ => return acc.add_resolution(ctx, name, def),
};
let name_ = name.to_smol_str();
let find = DEFAULT_DERIVE_DEPENDENCIES
.iter()
.find(|derive_completion| derive_completion.label == name_);
match find {
Some(derive_completion) => {
let mut components = vec![derive_completion.label];
components.extend(derive_completion.dependencies.iter().filter(
|&&dependency| {
!ctx.existing_derives
.iter()
.map(|it| it.name(ctx.db))
.any(|it| it.to_smol_str() == dependency)
},
));
let lookup = components.join(", ");
let label = Itertools::intersperse(components.into_iter().rev(), ", ");
let mut item = CompletionItem::new(
SymbolKind::Derive,
ctx.source_range(),
SmolStr::from_iter(label),
);
if let Some(docs) = mac.docs(ctx.db) {
item.documentation(docs);
}
item.lookup_by(lookup);
item.add_to(acc);
}
None => acc.add_resolution(ctx, name, def),
}
});
acc.add_nameref_keywords(ctx);
}
}
}
struct DeriveDependencies {

View File

@ -142,7 +142,7 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
)?;
let ns_filter = |import: &LocatedImport| {
let kind = match ctx.path_kind() {
let path_kind = match ctx.path_kind() {
Some(kind) => kind,
None => {
return match import.original_item {
@ -151,7 +151,7 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
}
}
};
match (kind, import.original_item) {
match (path_kind, import.original_item) {
// Aren't handled in flyimport
(PathKind::Vis { .. } | PathKind::Use, _) => false,
// modules are always fair game
@ -173,6 +173,11 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
(PathKind::Attr { .. }, ItemInNs::Macros(mac)) => mac.is_attr(ctx.db),
(PathKind::Attr { .. }, _) => false,
(PathKind::Derive, ItemInNs::Macros(mac)) => {
mac.is_derive(ctx.db) && !ctx.existing_derives.contains(&mac)
}
(PathKind::Derive, _) => false,
}
};

View File

@ -63,7 +63,13 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
}
match kind {
Some(PathKind::Pat | PathKind::Attr { .. } | PathKind::Vis { .. } | PathKind::Use) => {
Some(
PathKind::Pat
| PathKind::Attr { .. }
| PathKind::Vis { .. }
| PathKind::Use
| PathKind::Derive,
) => {
return;
}
_ => {
@ -415,10 +421,10 @@ macro_rules! foo { () => {} }
fn main() { let _ = crate::$0 }
"#,
expect![[r##"
expect![[r#"
fn main() fn()
ma foo!() #[macro_export] macro_rules! foo
"##]],
ma foo!() macro_rules! foo
"#]],
);
}

View File

@ -19,10 +19,11 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
Some(PathCompletionCtx {
kind:
Some(
PathKind::Vis { .. }
| PathKind::Attr { .. }
PathKind::Attr { .. }
| PathKind::Derive
| PathKind::Pat
| PathKind::Use { .. }
| PathKind::Pat,
| PathKind::Vis { .. },
),
..
}) => return,
@ -207,12 +208,12 @@ mod macros {
macro_rules! concat { }
}
"#,
expect![[r##"
expect![[r#"
fn f() fn()
ma concat!() #[macro_export] macro_rules! concat
ma concat!() macro_rules! concat
md std
bt u32
"##]],
"#]],
);
}

View File

@ -12,6 +12,7 @@ use ide_db::{
famous_defs::FamousDefs,
RootDatabase,
};
use rustc_hash::FxHashSet;
use syntax::{
algo::{find_node_at_offset, non_trivia_sibling},
ast::{self, AttrKind, HasName, NameOrNameRef},
@ -43,11 +44,12 @@ pub(crate) enum Visible {
No,
}
#[derive(Copy, Clone, Debug)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub(super) enum PathKind {
Expr,
Type,
Attr { kind: AttrKind, annotated_item_kind: Option<SyntaxKind> },
Derive,
Mac,
Pat,
Vis { has_in_token: bool },
@ -126,7 +128,6 @@ pub(crate) struct CompletionContext<'a> {
/// The parent function of the cursor position if it exists.
pub(super) function_def: Option<ast::Fn>,
pub(super) attr: Option<ast::Attr>,
/// The parent impl of the cursor position if it exists.
pub(super) impl_def: Option<ast::Impl>,
/// The NameLike under the cursor in the original file if it exists.
@ -142,6 +143,8 @@ pub(crate) struct CompletionContext<'a> {
pub(super) pattern_ctx: Option<PatternContext>,
pub(super) path_context: Option<PathCompletionCtx>,
pub(super) existing_derives: FxHashSet<hir::Macro>,
pub(super) locals: Vec<(Name, Local)>,
no_completion_required: bool,
@ -439,7 +442,6 @@ impl<'a> CompletionContext<'a> {
expected_name: None,
expected_type: None,
function_def: None,
attr: None,
impl_def: None,
name_syntax: None,
lifetime_ctx: None,
@ -452,6 +454,7 @@ impl<'a> CompletionContext<'a> {
locals,
incomplete_let: false,
no_completion_required: false,
existing_derives: Default::default(),
};
ctx.expand_and_fill(
original_file.syntax().clone(),
@ -472,6 +475,8 @@ impl<'a> CompletionContext<'a> {
mut fake_ident_token: SyntaxToken,
) {
let _p = profile::span("CompletionContext::expand_and_fill");
let mut derive_ctx = None;
'expansion: loop {
let parent_item =
|item: &ast::Item| item.syntax().ancestors().skip(1).find_map(ast::Item::cast);
@ -509,11 +514,45 @@ impl<'a> CompletionContext<'a> {
_ => break 'expansion,
}
}
let orig_tt = match find_node_at_offset::<ast::TokenTree>(&original_file, offset) {
Some(it) => it,
None => break,
};
let spec_tt = match find_node_at_offset::<ast::TokenTree>(&speculative_file, offset) {
Some(it) => it,
None => break,
};
// Expand pseudo-derive expansion
if let (Some(orig_attr), Some(spec_attr)) = (
orig_tt.syntax().parent().and_then(ast::Meta::cast).and_then(|it| it.parent_attr()),
spec_tt.syntax().parent().and_then(ast::Meta::cast).and_then(|it| it.parent_attr()),
) {
match (
self.sema.expand_derive_as_pseudo_attr_macro(&orig_attr),
self.sema.speculative_expand_derive_as_pseudo_attr_macro(
&orig_attr,
&spec_attr,
fake_ident_token.clone(),
),
) {
// Clearly not a derive macro
(None, None) => (),
// successful expansions
(Some(actual_expansion), Some((fake_expansion, fake_mapped_token))) => {
let new_offset = fake_mapped_token.text_range().start();
derive_ctx = Some((actual_expansion, fake_expansion, new_offset));
break 'expansion;
}
// exactly one expansion failed, inconsistent state so stop expanding completely
_ => break 'expansion,
}
}
// Expand fn-like macro calls
if let (Some(actual_macro_call), Some(macro_call_with_fake_ident)) = (
find_node_at_offset::<ast::MacroCall>(&original_file, offset),
find_node_at_offset::<ast::MacroCall>(&speculative_file, offset),
orig_tt.syntax().ancestors().find_map(ast::MacroCall::cast),
spec_tt.syntax().ancestors().find_map(ast::MacroCall::cast),
) {
let mac_call_path0 = actual_macro_call.path().as_ref().map(|s| s.syntax().text());
let mac_call_path1 =
@ -553,7 +592,7 @@ impl<'a> CompletionContext<'a> {
break;
}
self.fill(&original_file, speculative_file, offset);
self.fill(&original_file, speculative_file, offset, derive_ctx);
}
fn expected_type_and_name(&self) -> (Option<Type>, Option<NameOrNameRef>) {
@ -697,6 +736,7 @@ impl<'a> CompletionContext<'a> {
original_file: &SyntaxNode,
file_with_fake_ident: SyntaxNode,
offset: TextSize,
derive_ctx: Option<(SyntaxNode, SyntaxNode, TextSize)>,
) {
let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased().unwrap();
let syntax_element = NodeOrToken::Token(fake_ident_token);
@ -708,11 +748,6 @@ impl<'a> CompletionContext<'a> {
(fn_is_prev && !inside_impl_trait_block) || for_is_prev2
};
self.attr = self
.sema
.token_ancestors_with_macros(self.token.clone())
.take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE)
.find_map(ast::Attr::cast);
self.fake_attribute_under_caret = syntax_element.ancestors().find_map(ast::Attr::cast);
self.incomplete_let =
@ -724,6 +759,33 @@ impl<'a> CompletionContext<'a> {
self.expected_type = expected_type;
self.expected_name = expected_name;
// Overwrite the path kind for derives
if let Some((original_file, file_with_fake_ident, offset)) = derive_ctx {
let attr = self
.sema
.token_ancestors_with_macros(self.token.clone())
.take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE)
.find_map(ast::Attr::cast);
if let Some(attr) = &attr {
self.existing_derives =
self.sema.resolve_derive_macro(attr).into_iter().flatten().flatten().collect();
}
if let Some(ast::NameLike::NameRef(name_ref)) =
find_node_at_offset(&file_with_fake_ident, offset)
{
self.name_syntax =
find_node_at_offset(&original_file, name_ref.syntax().text_range().start());
if let Some((path_ctx, _)) =
Self::classify_name_ref(&self.sema, &original_file, name_ref)
{
self.path_context =
Some(PathCompletionCtx { kind: Some(PathKind::Derive), ..path_ctx });
}
}
return;
}
let name_like = match find_node_at_offset(&file_with_fake_ident, offset) {
Some(it) => it,
None => return,
@ -743,6 +805,7 @@ impl<'a> CompletionContext<'a> {
.token_ancestors_with_macros(self.token.clone())
.take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE)
.find_map(ast::Fn::cast);
match name_like {
ast::NameLike::Lifetime(lifetime) => {
self.lifetime_ctx =

View File

@ -152,6 +152,7 @@ pub fn completions(
let mut acc = Completions::default();
completions::attribute::complete_attribute(&mut acc, &ctx);
completions::attribute::complete_derive(&mut acc, &ctx);
completions::attribute::complete_known_attribute_input(&mut acc, &ctx);
completions::dot::complete_dot(&mut acc, &ctx);
completions::extern_abi::complete_extern_abi(&mut acc, &ctx);

View File

@ -1,12 +1,8 @@
//! Renderer for macro invocations.
use either::Either;
use hir::{Documentation, HasSource, InFile, Semantics};
use ide_db::{RootDatabase, SymbolKind};
use syntax::{
display::{fn_as_proc_macro_label, macro_label},
SmolStr,
};
use hir::{Documentation, HirDisplay};
use ide_db::SymbolKind;
use syntax::SmolStr;
use crate::{
context::PathKind,
@ -52,7 +48,7 @@ fn render(
label(&ctx, needs_bang, bra, ket, &name),
);
item.set_deprecated(ctx.is_deprecated(macro_))
.set_detail(detail(&completion.sema, macro_))
.detail(macro_.display(completion.db).to_string())
.set_documentation(docs)
.set_relevance(ctx.completion_relevance());
@ -103,17 +99,6 @@ fn banged_name(name: &str) -> SmolStr {
SmolStr::from_iter([name, "!"])
}
fn detail(sema: &Semantics<RootDatabase>, macro_: hir::Macro) -> Option<String> {
// FIXME: This is parsing the file!
let InFile { file_id, value } = macro_.source(sema.db)?;
let _ = sema.parse_or_expand(file_id);
let detail = match value {
Either::Left(node) => macro_label(&node),
Either::Right(node) => fn_as_proc_macro_label(&node),
};
Some(detail)
}
fn guess_macro_braces(macro_name: &str, docs: &str) -> (&'static str, &'static str) {
let mut votes = [0, 0, 0];
for (idx, s) in docs.match_indices(&macro_name) {

View File

@ -62,7 +62,7 @@ fn proc_macros_qualified() {
struct Foo;
"#,
expect![[r#"
at identity pub macro identity
at identity proc_macro identity
"#]],
)
}
@ -302,7 +302,7 @@ struct Foo;
"#,
expect![[r#"
md core
at derive pub macro derive
at derive macro derive
kw self::
kw super::
kw crate::
@ -688,13 +688,17 @@ mod derive {
#[derive($0)] struct Test;
"#,
expect![[r#"
de Default
md core
de Default macro Default
de Clone, Copy
de PartialEq
de PartialEq macro PartialEq
de PartialEq, Eq
de PartialEq, Eq, PartialOrd, Ord
de Clone
de Clone macro Clone
de PartialEq, PartialOrd
kw self::
kw super::
kw crate::
"#]],
);
}
@ -707,12 +711,16 @@ mod derive {
#[derive(serde::Serialize, PartialEq, $0)] struct Test;
"#,
expect![[r#"
de Default
md core
de Default macro Default
de Clone, Copy
de Eq
de Eq, PartialOrd, Ord
de Clone
de Clone macro Clone
de PartialOrd
kw self::
kw super::
kw crate::
"#]],
)
}
@ -725,33 +733,17 @@ mod derive {
#[derive($0 serde::Serialize, PartialEq)] struct Test;
"#,
expect![[r#"
de Default
md core
de Default macro Default
de Clone, Copy
de Eq
de Eq, PartialOrd, Ord
de Clone
de Clone macro Clone
de PartialOrd
kw self::
kw super::
kw crate::
"#]],
)
}
#[test]
fn derive_no_attrs() {
check_derive(
r#"
//- proc_macros: identity
//- minicore: derive
#[derive($0)] struct Test;
"#,
expect![[r#""#]],
);
check_derive(
r#"
//- proc_macros: identity
//- minicore: derive
#[derive(i$0)] struct Test;
"#,
expect![[r#""#]],
);
}
@ -760,20 +752,32 @@ mod derive {
check_derive(
r#"
//- proc_macros: derive_identity
//- minicore: derive
#[derive(der$0)] struct Test;
"#,
expect![[r#"
de DeriveIdentity (use proc_macros::DeriveIdentity)
md proc_macros
md core
kw self::
kw super::
kw crate::
de DeriveIdentity (use proc_macros::DeriveIdentity) proc_macro DeriveIdentity
"#]],
);
check_derive(
r#"
//- proc_macros: derive_identity
//- minicore: derive
use proc_macros::DeriveIdentity;
#[derive(der$0)] struct Test;
"#,
expect![[r#"
de DeriveIdentity
de DeriveIdentity proc_macro DeriveIdentity
md proc_macros
md core
kw self::
kw super::
kw crate::
"#]],
);
}
@ -784,6 +788,7 @@ use proc_macros::DeriveIdentity;
"DeriveIdentity",
r#"
//- proc_macros: derive_identity
//- minicore: derive
#[derive(der$0)] struct Test;
"#,
r#"
@ -793,6 +798,30 @@ use proc_macros::DeriveIdentity;
"#,
);
}
#[test]
fn qualified() {
check_derive(
r#"
//- proc_macros: derive_identity
//- minicore: derive, copy, clone
#[derive(proc_macros::$0)] struct Test;
"#,
expect![[r#"
de DeriveIdentity proc_macro DeriveIdentity
"#]],
);
check_derive(
r#"
//- proc_macros: derive_identity
//- minicore: derive, copy, clone
#[derive(proc_macros::C$0)] struct Test;
"#,
expect![[r#"
de DeriveIdentity proc_macro DeriveIdentity
"#]],
);
}
}
mod lint {

View File

@ -30,7 +30,7 @@ fn baz() {
}
"#,
// This should not contain `FooDesc {…}`.
expect![[r##"
expect![[r#"
kw unsafe
kw match
kw while
@ -57,13 +57,13 @@ fn baz() {
fn baz() fn()
st Unit
md _69latrick
ma makro!() #[macro_export] macro_rules! makro
ma makro!() macro_rules! makro
fn function() fn()
sc STATIC
un Union
ev TupleV() (u32)
ct CONST
"##]],
"#]],
)
}
@ -125,7 +125,7 @@ impl Unit {
}
"#,
// `self` is in here twice, once as the module, once as the local
expect![[r##"
expect![[r#"
me self.foo() fn(self)
kw unsafe
kw fn
@ -166,14 +166,14 @@ impl Unit {
md module
st Unit
md qualified
ma makro!() #[macro_export] macro_rules! makro
ma makro!() macro_rules! makro
?? Unresolved
fn function() fn()
sc STATIC
un Union
ev TupleV() (u32)
ct CONST
"##]],
"#]],
);
check(
r#"
@ -187,7 +187,7 @@ impl Unit {
}
}
"#,
expect![[r##"
expect![[r#"
tt Trait
en Enum
st Record
@ -195,14 +195,14 @@ impl Unit {
md module
st Unit
md qualified
ma makro!() #[macro_export] macro_rules! makro
ma makro!() macro_rules! makro
?? Unresolved
fn function() fn()
sc STATIC
un Union
ev TupleV() (u32)
ct CONST
"##]],
"#]],
);
}

View File

@ -1108,7 +1108,7 @@ fn flyimport_attribute() {
struct Foo;
"#,
expect![[r#"
at identity (use proc_macros::identity) pub macro identity
at identity (use proc_macros::identity) proc_macro identity
"#]],
);
check_edit(

View File

@ -17,7 +17,7 @@ fn target_type_or_trait_in_impl_block() {
r#"
impl Tra$0
"#,
expect![[r##"
expect![[r#"
kw self
kw super
kw crate
@ -27,10 +27,10 @@ impl Tra$0
st Tuple
md module
st Unit
ma makro!() #[macro_export] macro_rules! makro
ma makro!() macro_rules! makro
un Union
bt u32
"##]],
"#]],
)
}
@ -40,7 +40,7 @@ fn target_type_in_trait_impl_block() {
r#"
impl Trait for Str$0
"#,
expect![[r##"
expect![[r#"
kw self
kw super
kw crate
@ -50,10 +50,10 @@ impl Trait for Str$0
st Tuple
md module
st Unit
ma makro!() #[macro_export] macro_rules! makro
ma makro!() macro_rules! makro
un Union
bt u32
"##]],
"#]],
)
}
@ -85,7 +85,7 @@ fn after_struct_name() {
// FIXME: This should emit `kw where` only
check(
r"struct Struct $0",
expect![[r##"
expect![[r#"
kw pub(crate)
kw pub(super)
kw pub
@ -109,8 +109,8 @@ fn after_struct_name() {
kw super
kw crate
md module
ma makro!() #[macro_export] macro_rules! makro
"##]],
ma makro!() macro_rules! makro
"#]],
);
}
@ -119,7 +119,7 @@ fn after_fn_name() {
// FIXME: This should emit `kw where` only
check(
r"fn func() $0",
expect![[r##"
expect![[r#"
kw pub(crate)
kw pub(super)
kw pub
@ -143,8 +143,8 @@ fn after_fn_name() {
kw super
kw crate
md module
ma makro!() #[macro_export] macro_rules! makro
"##]],
ma makro!() macro_rules! makro
"#]],
);
}

View File

@ -12,7 +12,7 @@ fn check(ra_fixture: &str, expect: Expect) {
fn in_mod_item_list() {
check(
r#"mod tests { $0 }"#,
expect![[r##"
expect![[r#"
kw pub(crate)
kw pub(super)
kw pub
@ -35,8 +35,8 @@ fn in_mod_item_list() {
kw self
kw super
kw crate
ma makro!() #[macro_export] macro_rules! makro
"##]],
ma makro!() macro_rules! makro
"#]],
)
}
@ -44,7 +44,7 @@ fn in_mod_item_list() {
fn in_source_file_item_list() {
check(
r#"$0"#,
expect![[r##"
expect![[r#"
kw pub(crate)
kw pub(super)
kw pub
@ -68,8 +68,8 @@ fn in_source_file_item_list() {
kw super
kw crate
md module
ma makro!() #[macro_export] macro_rules! makro
"##]],
ma makro!() macro_rules! makro
"#]],
)
}
@ -106,10 +106,10 @@ fn in_qualified_path() {
cov_mark::check!(no_keyword_completion_in_non_trivial_path);
check(
r#"crate::$0"#,
expect![[r##"
expect![[r#"
md module
ma makro!() #[macro_export] macro_rules! makro
"##]],
ma makro!() macro_rules! makro
"#]],
)
}
@ -162,7 +162,7 @@ fn after_visibility_unsafe() {
fn in_impl_assoc_item_list() {
check(
r#"impl Struct { $0 }"#,
expect![[r##"
expect![[r#"
kw pub(crate)
kw pub(super)
kw pub
@ -174,8 +174,8 @@ fn in_impl_assoc_item_list() {
kw super
kw crate
md module
ma makro!() #[macro_export] macro_rules! makro
"##]],
ma makro!() macro_rules! makro
"#]],
)
}
@ -199,7 +199,7 @@ fn in_impl_assoc_item_list_after_attr() {
fn in_trait_assoc_item_list() {
check(
r"trait Foo { $0 }",
expect![[r##"
expect![[r#"
kw unsafe
kw fn
kw const
@ -208,8 +208,8 @@ fn in_trait_assoc_item_list() {
kw super
kw crate
md module
ma makro!() #[macro_export] macro_rules! makro
"##]],
ma makro!() macro_rules! makro
"#]],
);
}
@ -233,7 +233,7 @@ impl Test for () {
$0
}
"#,
expect![[r##"
expect![[r#"
kw pub(crate)
kw pub(super)
kw pub
@ -245,7 +245,7 @@ impl Test for () {
kw super
kw crate
md module
ma makro!() #[macro_export] macro_rules! makro
"##]],
ma makro!() macro_rules! makro
"#]],
);
}

View File

@ -102,7 +102,7 @@ fn foo() {
if let a$0
}
"#,
expect![[r##"
expect![[r#"
kw ref
kw mut
en Enum
@ -112,11 +112,11 @@ fn foo() {
st Tuple
md module
st Unit
ma makro!() #[macro_export] macro_rules! makro
ma makro!() macro_rules! makro
bn TupleV TupleV($1)$0
ev TupleV
ct CONST
"##]],
"#]],
);
}
@ -132,7 +132,7 @@ fn foo() {
let a$0
}
"#,
expect![[r##"
expect![[r#"
kw ref
kw mut
bn Record Record { field$1 }$0
@ -142,8 +142,8 @@ fn foo() {
ev Variant
en SingleVariantEnum
st Unit
ma makro!() #[macro_export] macro_rules! makro
"##]],
ma makro!() macro_rules! makro
"#]],
);
}
@ -154,7 +154,7 @@ fn in_param() {
fn foo(a$0) {
}
"#,
expect![[r##"
expect![[r#"
kw ref
kw mut
bn Record Record { field$1 }: Record$0
@ -162,15 +162,15 @@ fn foo(a$0) {
bn Tuple Tuple($1): Tuple$0
st Tuple
st Unit
ma makro!() #[macro_export] macro_rules! makro
"##]],
ma makro!() macro_rules! makro
"#]],
);
check(
r#"
fn foo(a$0: Tuple) {
}
"#,
expect![[r##"
expect![[r#"
kw ref
kw mut
bn Record Record { field$1 }$0
@ -178,8 +178,8 @@ fn foo(a$0: Tuple) {
bn Tuple Tuple($1)$0
st Tuple
st Unit
ma makro!() #[macro_export] macro_rules! makro
"##]],
ma makro!() macro_rules! makro
"#]],
);
}

View File

@ -15,7 +15,7 @@ fn predicate_start() {
r#"
struct Foo<'lt, T, const C: usize> where $0 {}
"#,
expect![[r##"
expect![[r#"
kw self
kw super
kw crate
@ -26,10 +26,10 @@ struct Foo<'lt, T, const C: usize> where $0 {}
md module
st Foo<>
st Unit
ma makro!() #[macro_export] macro_rules! makro
ma makro!() macro_rules! makro
un Union
bt u32
"##]],
"#]],
);
}
@ -39,14 +39,14 @@ fn bound_for_type_pred() {
r#"
struct Foo<'lt, T, const C: usize> where T: $0 {}
"#,
expect![[r##"
expect![[r#"
kw self
kw super
kw crate
tt Trait
md module
ma makro!() #[macro_export] macro_rules! makro
"##]],
ma makro!() macro_rules! makro
"#]],
);
}
@ -58,14 +58,14 @@ fn bound_for_lifetime_pred() {
r#"
struct Foo<'lt, T, const C: usize> where 'lt: $0 {}
"#,
expect![[r##"
expect![[r#"
kw self
kw super
kw crate
tt Trait
md module
ma makro!() #[macro_export] macro_rules! makro
"##]],
ma makro!() macro_rules! makro
"#]],
);
}
@ -75,14 +75,14 @@ fn bound_for_for_pred() {
r#"
struct Foo<'lt, T, const C: usize> where for<'a> T: $0 {}
"#,
expect![[r##"
expect![[r#"
kw self
kw super
kw crate
tt Trait
md module
ma makro!() #[macro_export] macro_rules! makro
"##]],
ma makro!() macro_rules! makro
"#]],
);
}
@ -92,7 +92,7 @@ fn param_list_for_for_pred() {
r#"
struct Foo<'lt, T, const C: usize> where for<'a> $0 {}
"#,
expect![[r##"
expect![[r#"
kw self
kw super
kw crate
@ -103,10 +103,10 @@ struct Foo<'lt, T, const C: usize> where for<'a> $0 {}
md module
st Foo<>
st Unit
ma makro!() #[macro_export] macro_rules! makro
ma makro!() macro_rules! makro
un Union
bt u32
"##]],
"#]],
);
}
@ -118,7 +118,7 @@ impl Record {
fn method(self) where $0 {}
}
"#,
expect![[r##"
expect![[r#"
kw self
kw super
kw crate
@ -129,9 +129,9 @@ impl Record {
st Tuple
md module
st Unit
ma makro!() #[macro_export] macro_rules! makro
ma makro!() macro_rules! makro
un Union
bt u32
"##]],
"#]],
);
}

View File

@ -16,7 +16,7 @@ struct Foo<'lt, T, const C: usize> {
f: $0
}
"#,
expect![[r##"
expect![[r#"
kw self
kw super
kw crate
@ -29,10 +29,10 @@ struct Foo<'lt, T, const C: usize> {
md module
st Foo<>
st Unit
ma makro!() #[macro_export] macro_rules! makro
ma makro!() macro_rules! makro
un Union
bt u32
"##]],
"#]],
)
}
@ -42,7 +42,7 @@ fn tuple_struct_field() {
r#"
struct Foo<'lt, T, const C: usize>(f$0);
"#,
expect![[r##"
expect![[r#"
kw pub(crate)
kw pub(super)
kw pub
@ -58,10 +58,10 @@ struct Foo<'lt, T, const C: usize>(f$0);
md module
st Foo<>
st Unit
ma makro!() #[macro_export] macro_rules! makro
ma makro!() macro_rules! makro
un Union
bt u32
"##]],
"#]],
)
}
@ -71,7 +71,7 @@ fn fn_return_type() {
r#"
fn x<'lt, T, const C: usize>() -> $0
"#,
expect![[r##"
expect![[r#"
kw self
kw super
kw crate
@ -82,10 +82,10 @@ fn x<'lt, T, const C: usize>() -> $0
st Tuple
md module
st Unit
ma makro!() #[macro_export] macro_rules! makro
ma makro!() macro_rules! makro
un Union
bt u32
"##]],
"#]],
);
}
@ -98,7 +98,7 @@ fn foo<'lt, T, const C: usize>() {
let _: $0;
}
"#,
expect![[r##"
expect![[r#"
kw self
kw super
kw crate
@ -109,10 +109,10 @@ fn foo<'lt, T, const C: usize>() {
st Tuple
md module
st Unit
ma makro!() #[macro_export] macro_rules! makro
ma makro!() macro_rules! makro
un Union
bt u32
"##]],
"#]],
);
check(
r#"
@ -121,16 +121,16 @@ fn foo<'lt, T, const C: usize>() {
let _: self::$0;
}
"#,
expect![[r##"
expect![[r#"
tt Trait
en Enum
st Record
st Tuple
md module
st Unit
ma makro!() #[macro_export] macro_rules! makro
ma makro!() macro_rules! makro
un Union
"##]],
"#]],
);
}
@ -144,7 +144,7 @@ trait Trait2 {
fn foo<'lt, T: Trait2<$0>, const CONST_PARAM: usize>(_: T) {}
"#,
expect![[r##"
expect![[r#"
kw self
kw super
kw crate
@ -157,12 +157,12 @@ fn foo<'lt, T: Trait2<$0>, const CONST_PARAM: usize>(_: T) {}
st Tuple
md module
st Unit
ma makro!() #[macro_export] macro_rules! makro
ma makro!() macro_rules! makro
tt Trait2
un Union
ct CONST
bt u32
"##]],
"#]],
);
check(
r#"
@ -172,18 +172,18 @@ trait Trait2 {
fn foo<'lt, T: Trait2<self::$0>, const CONST_PARAM: usize>(_: T) {}
"#,
expect![[r##"
expect![[r#"
tt Trait
en Enum
st Record
st Tuple
md module
st Unit
ma makro!() #[macro_export] macro_rules! makro
ma makro!() macro_rules! makro
tt Trait2
un Union
ct CONST
"##]],
"#]],
);
}

View File

@ -443,7 +443,7 @@ pub fn parse_tt_as_comma_sep_paths(input: ast::TokenTree) -> Option<Vec<ast::Pat
None => None,
Some(tok) => Some(tok),
});
let input_expressions = tokens.into_iter().group_by(|tok| tok.kind() == T![,]);
let input_expressions = tokens.group_by(|tok| tok.kind() == T![,]);
let paths = input_expressions
.into_iter()
.filter_map(|(is_sep, group)| (!is_sep).then(|| group))

View File

@ -1,6 +1,6 @@
//! This module contains utilities for rendering syntax nodes into a string representing their signature.
use crate::ast::{self, HasAttrs, HasGenericParams, HasName};
use crate::ast::{self, HasGenericParams, HasName};
use ast::HasVisibility;
use stdx::format_to;
@ -49,37 +49,3 @@ pub fn function_declaration(node: &ast::Fn) -> String {
}
buf
}
pub fn macro_label(node: &ast::Macro) -> String {
let name = node.name();
let mut s = String::new();
match node {
ast::Macro::MacroRules(node) => {
let vis = if node.has_atom_attr("macro_export") { "#[macro_export] " } else { "" };
format_to!(s, "{}macro_rules!", vis);
}
ast::Macro::MacroDef(node) => {
if let Some(vis) = node.visibility() {
format_to!(s, "{} ", vis);
}
format_to!(s, "macro");
}
}
if let Some(name) = name {
format_to!(s, " {}", name);
}
s
}
pub fn fn_as_proc_macro_label(node: &ast::Fn) -> String {
let name = node.name();
let mut s = String::new();
if let Some(vis) = node.visibility() {
format_to!(s, "{} ", vis);
}
format_to!(s, "macro");
if let Some(name) = name {
format_to!(s, " {}", name);
}
s
}