From: Lukas Wirth Date: Mon, 21 Feb 2022 11:57:57 +0000 (+0100) Subject: Make replace_derive_with_manual_impl work again X-Git-Url: https://git.lizzy.rs/?a=commitdiff_plain;ds=sidebyside;h=f13c98034bff751cfb617409211f2beb9933818a;p=rust.git Make replace_derive_with_manual_impl work again --- diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index f047971a116..423b46cc618 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -1792,6 +1792,13 @@ pub fn is_fn_like(&self) -> bool { } } + pub fn is_builtin_derive(&self) -> bool { + match self.id.kind { + MacroDefKind::BuiltInAttr(exp, _) => exp.is_derive(), + _ => false, + } + } + pub fn is_attr(&self) -> bool { matches!(self.kind(), MacroKind::Attr) } diff --git a/crates/hir_expand/src/lib.rs b/crates/hir_expand/src/lib.rs index 2c6378cd3ee..8803dac0975 100644 --- a/crates/hir_expand/src/lib.rs +++ b/crates/hir_expand/src/lib.rs @@ -386,6 +386,17 @@ pub fn to_node(&self, db: &dyn db::AstDatabase) -> InFile { MacroCallKind::Derive { ast_id, .. } => { ast_id.with_value(ast_id.to_node(db).syntax().clone()) } + MacroCallKind::Attr { ast_id, is_derive: true, invoc_attr_index, .. } => { + ast_id.with_value(ast_id.to_node(db)).map(|it| { + it.doc_comments_and_attrs() + .nth(*invoc_attr_index as usize) + .and_then(|it| match it { + Either::Left(attr) => Some(attr.syntax().clone()), + Either::Right(_) => None, + }) + .unwrap_or_else(|| it.syntax().clone()) + }) + } MacroCallKind::Attr { ast_id, .. } => { ast_id.with_value(ast_id.to_node(db).syntax().clone()) } diff --git a/crates/ide/src/expand_macro.rs b/crates/ide/src/expand_macro.rs index c234eb3db6b..f7326747253 100644 --- a/crates/ide/src/expand_macro.rs +++ b/crates/ide/src/expand_macro.rs @@ -3,8 +3,7 @@ helpers::{insert_whitespace_into_node::insert_ws_into, pick_best_token}, RootDatabase, }; -use itertools::Itertools; -use syntax::{ast, ted, AstNode, SyntaxKind, SyntaxNode}; +use syntax::{ast, ted, AstNode, NodeOrToken, SyntaxKind, SyntaxNode, T}; use crate::FilePosition; @@ -52,7 +51,17 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option< let token = hir::InFile::new(hir_file, descended).upmap(db)?.value; let attr = token.ancestors().find_map(ast::Attr::cast)?; let expansions = sema.expand_derive_macro(&attr)?; - Some(ExpandedMacro { name, expansion: expansions.into_iter().map(insert_ws_into).join("") }) + let idx = attr + .token_tree()? + .token_trees_and_tokens() + .filter_map(NodeOrToken::into_token) + .take_while(|it| it == &token) + .filter(|it| it.kind() == T![,]) + .count(); + Some(ExpandedMacro { + name, + expansion: expansions.get(idx).cloned().map(insert_ws_into)?.to_string(), + }) }); if derive.is_some() { @@ -370,11 +379,9 @@ fn macro_expand_derive_multi() { struct Foo {} "#, expect![[r#" - Copy, Clone + Copy impl < >core::marker::Copy for Foo< >{} - impl < >core::clone::Clone for Foo< >{} - "#]], ); } diff --git a/crates/ide_assists/src/handlers/auto_import.rs b/crates/ide_assists/src/handlers/auto_import.rs index cac736ff850..9c0233b028f 100644 --- a/crates/ide_assists/src/handlers/auto_import.rs +++ b/crates/ide_assists/src/handlers/auto_import.rs @@ -3,7 +3,7 @@ insert_use::{insert_use, ImportScope}, mod_path_to_ast, }; -use syntax::{ast, AstNode, AstToken, NodeOrToken, SyntaxElement}; +use syntax::{ast, AstNode, NodeOrToken, SyntaxElement}; use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel}; @@ -139,9 +139,7 @@ pub(super) fn find_importable_node(ctx: &AssistContext) -> Option<(ImportAssets, { ImportAssets::for_ident_pat(&ctx.sema, &pat).zip(Some(pat.syntax().clone().into())) } else { - // FIXME: Descend? - let ident = ctx.find_token_at_offset()?; - ImportAssets::for_derive_ident(&ctx.sema, &ident).zip(Some(ident.syntax().clone().into())) + None } } diff --git a/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs b/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs index b3723710a86..8ac05bf5ff5 100644 --- a/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs +++ b/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs @@ -1,13 +1,13 @@ -use hir::ModuleDef; -use ide_db::helpers::insert_whitespace_into_node::insert_ws_into; -use ide_db::helpers::{ - get_path_at_cursor_in_tt, import_assets::NameToImport, mod_path_to_ast, - parse_tt_as_comma_sep_paths, +use hir::{InFile, ModuleDef}; +use ide_db::{ + helpers::{ + import_assets::NameToImport, insert_whitespace_into_node::insert_ws_into, mod_path_to_ast, + }, + items_locator, }; -use ide_db::items_locator; use itertools::Itertools; use syntax::{ - ast::{self, AstNode, AstToken, HasName}, + ast::{self, AstNode, HasName}, SyntaxKind::WHITESPACE, }; @@ -25,6 +25,7 @@ // Converts a `derive` impl into a manual one. // // ``` +// # //- minicore: derive // # trait Debug { fn fmt(&self, f: &mut Formatter) -> Result<()>; } // #[derive(Deb$0ug, Display)] // struct S; @@ -45,20 +46,30 @@ pub(crate) fn replace_derive_with_manual_impl( acc: &mut Assists, ctx: &AssistContext, ) -> Option<()> { - let attr = ctx.find_node_at_offset::()?; - let (name, args) = attr.as_simple_call()?; - if name != "derive" { + let attr = ctx.find_node_at_offset_with_descend::()?; + let path = attr.path()?; + let hir_file = ctx.sema.hir_file_for(attr.syntax()); + if !hir_file.is_derive_attr_macro(ctx.db()) { return None; } - if !args.syntax().text_range().contains(ctx.offset()) { - cov_mark::hit!(outside_of_attr_args); + let InFile { file_id, value } = hir_file.call_node(ctx.db())?; + if file_id.is_macro() { + // FIXME: make this work in macro files return None; } + // collect the derive paths from the #[derive] expansion + let current_derives = ctx + .sema + .parse_or_expand(hir_file)? + .descendants() + .filter_map(ast::Attr::cast) + .filter_map(|attr| attr.path()) + .collect::>(); - let ident = args.syntax().token_at_offset(ctx.offset()).find_map(ast::Ident::cast)?; - let trait_path = get_path_at_cursor_in_tt(&ident)?; - let adt = attr.syntax().parent().and_then(ast::Adt::cast)?; + let adt = value.parent().and_then(ast::Adt::cast)?; + let attr = ast::Attr::cast(value)?; + let args = attr.token_tree()?; let current_module = ctx.sema.scope(adt.syntax()).module()?; let current_crate = current_module.krate(); @@ -66,7 +77,7 @@ pub(crate) fn replace_derive_with_manual_impl( let found_traits = items_locator::items_with_name( &ctx.sema, current_crate, - NameToImport::exact_case_sensitive(trait_path.segments().last()?.to_string()), + NameToImport::exact_case_sensitive(path.segments().last()?.to_string()), items_locator::AssocItemSearch::Exclude, Some(items_locator::DEFAULT_QUERY_SEARCH_LIMIT.inner()), ) @@ -83,8 +94,6 @@ pub(crate) fn replace_derive_with_manual_impl( }); let mut no_traits_found = true; - let current_derives = parse_tt_as_comma_sep_paths(args.clone())?; - let current_derives = current_derives.as_slice(); for (replace_trait_path, trait_) in found_traits.inspect(|_| no_traits_found = false) { add_assist( acc, @@ -92,14 +101,14 @@ pub(crate) fn replace_derive_with_manual_impl( &attr, ¤t_derives, &args, - &trait_path, + &path, &replace_trait_path, Some(trait_), &adt, )?; } if no_traits_found { - add_assist(acc, ctx, &attr, ¤t_derives, &args, &trait_path, &trait_path, None, &adt)?; + add_assist(acc, ctx, &attr, ¤t_derives, &args, &path, &path, None, &adt)?; } Some(()) } @@ -128,7 +137,7 @@ fn add_assist( let impl_def_with_items = impl_def_from_trait(&ctx.sema, adt, &annotated_name, trait_, replace_trait_path); update_attribute(builder, old_derives, old_tree, old_trait_path, attr); - let trait_path = format!("{}", replace_trait_path); + let trait_path = replace_trait_path.to_string(); match (ctx.config.snippet_cap, impl_def_with_items) { (None, _) => { builder.insert(insert_pos, generate_trait_impl_text(adt, &trait_path, "")) @@ -258,7 +267,7 @@ fn add_custom_impl_debug_record_struct() { check_assist( replace_derive_with_manual_impl, r#" -//- minicore: fmt +//- minicore: fmt, derive #[derive(Debu$0g)] struct Foo { bar: String, @@ -282,7 +291,7 @@ fn add_custom_impl_debug_tuple_struct() { check_assist( replace_derive_with_manual_impl, r#" -//- minicore: fmt +//- minicore: fmt, derive #[derive(Debu$0g)] struct Foo(String, usize); "#, @@ -301,7 +310,7 @@ fn add_custom_impl_debug_empty_struct() { check_assist( replace_derive_with_manual_impl, r#" -//- minicore: fmt +//- minicore: fmt, derive #[derive(Debu$0g)] struct Foo; "#, @@ -321,7 +330,7 @@ fn add_custom_impl_debug_enum() { check_assist( replace_derive_with_manual_impl, r#" -//- minicore: fmt +//- minicore: fmt, derive #[derive(Debu$0g)] enum Foo { Bar, @@ -351,7 +360,7 @@ fn add_custom_impl_debug_tuple_enum() { check_assist( replace_derive_with_manual_impl, r#" -//- minicore: fmt +//- minicore: fmt, derive #[derive(Debu$0g)] enum Foo { Bar(usize, usize), @@ -380,7 +389,7 @@ fn add_custom_impl_debug_record_enum() { check_assist( replace_derive_with_manual_impl, r#" -//- minicore: fmt +//- minicore: fmt, derive #[derive(Debu$0g)] enum Foo { Bar { @@ -415,7 +424,7 @@ fn add_custom_impl_default_record_struct() { check_assist( replace_derive_with_manual_impl, r#" -//- minicore: default +//- minicore: default, derive #[derive(Defau$0lt)] struct Foo { foo: usize, @@ -439,7 +448,7 @@ fn add_custom_impl_default_tuple_struct() { check_assist( replace_derive_with_manual_impl, r#" -//- minicore: default +//- minicore: default, derive #[derive(Defau$0lt)] struct Foo(usize); "#, @@ -459,7 +468,7 @@ fn add_custom_impl_default_empty_struct() { check_assist( replace_derive_with_manual_impl, r#" -//- minicore: default +//- minicore: default, derive #[derive(Defau$0lt)] struct Foo; "#, @@ -480,7 +489,7 @@ fn add_custom_impl_hash_record_struct() { check_assist( replace_derive_with_manual_impl, r#" -//- minicore: hash +//- minicore: hash, derive #[derive(Has$0h)] struct Foo { bin: usize, @@ -508,7 +517,7 @@ fn add_custom_impl_hash_tuple_struct() { check_assist( replace_derive_with_manual_impl, r#" -//- minicore: hash +//- minicore: hash, derive #[derive(Has$0h)] struct Foo(usize, usize); "#, @@ -530,7 +539,7 @@ fn add_custom_impl_hash_enum() { check_assist( replace_derive_with_manual_impl, r#" -//- minicore: hash +//- minicore: hash, derive #[derive(Has$0h)] enum Foo { Bar, @@ -557,7 +566,7 @@ fn add_custom_impl_clone_record_struct() { check_assist( replace_derive_with_manual_impl, r#" -//- minicore: clone +//- minicore: clone, derive #[derive(Clo$0ne)] struct Foo { bin: usize, @@ -584,7 +593,7 @@ fn add_custom_impl_clone_tuple_struct() { check_assist( replace_derive_with_manual_impl, r#" -//- minicore: clone +//- minicore: clone, derive #[derive(Clo$0ne)] struct Foo(usize, usize); "#, @@ -605,7 +614,7 @@ fn add_custom_impl_clone_empty_struct() { check_assist( replace_derive_with_manual_impl, r#" -//- minicore: clone +//- minicore: clone, derive #[derive(Clo$0ne)] struct Foo; "#, @@ -626,7 +635,7 @@ fn add_custom_impl_clone_enum() { check_assist( replace_derive_with_manual_impl, r#" -//- minicore: clone +//- minicore: clone, derive #[derive(Clo$0ne)] enum Foo { Bar, @@ -656,7 +665,7 @@ fn add_custom_impl_clone_tuple_enum() { check_assist( replace_derive_with_manual_impl, r#" -//- minicore: clone +//- minicore: clone, derive #[derive(Clo$0ne)] enum Foo { Bar(String), @@ -686,7 +695,7 @@ fn add_custom_impl_clone_record_enum() { check_assist( replace_derive_with_manual_impl, r#" -//- minicore: clone +//- minicore: clone, derive #[derive(Clo$0ne)] enum Foo { Bar { @@ -720,7 +729,7 @@ fn add_custom_impl_partial_ord_record_struct() { check_assist( replace_derive_with_manual_impl, r#" -//- minicore: ord +//- minicore: ord, derive #[derive(Partial$0Ord)] struct Foo { bin: usize, @@ -745,7 +754,7 @@ fn add_custom_impl_partial_ord_record_struct_multi_field() { check_assist( replace_derive_with_manual_impl, r#" -//- minicore: ord +//- minicore: ord, derive #[derive(Partial$0Ord)] struct Foo { bin: usize, @@ -782,7 +791,7 @@ fn add_custom_impl_partial_ord_tuple_struct() { check_assist( replace_derive_with_manual_impl, r#" -//- minicore: ord +//- minicore: ord, derive #[derive(Partial$0Ord)] struct Foo(usize, usize, usize); "#, @@ -811,7 +820,7 @@ fn add_custom_impl_partial_eq_record_struct() { check_assist( replace_derive_with_manual_impl, r#" -//- minicore: eq +//- minicore: eq, derive #[derive(Partial$0Eq)] struct Foo { bin: usize, @@ -838,7 +847,7 @@ fn add_custom_impl_partial_eq_tuple_struct() { check_assist( replace_derive_with_manual_impl, r#" -//- minicore: eq +//- minicore: eq, derive #[derive(Partial$0Eq)] struct Foo(usize, usize); "#, @@ -859,7 +868,7 @@ fn add_custom_impl_partial_eq_empty_struct() { check_assist( replace_derive_with_manual_impl, r#" -//- minicore: eq +//- minicore: eq, derive #[derive(Partial$0Eq)] struct Foo; "#, @@ -880,7 +889,7 @@ fn add_custom_impl_partial_eq_enum() { check_assist( replace_derive_with_manual_impl, r#" -//- minicore: eq +//- minicore: eq, derive #[derive(Partial$0Eq)] enum Foo { Bar, @@ -907,7 +916,7 @@ fn add_custom_impl_partial_eq_tuple_enum() { check_assist( replace_derive_with_manual_impl, r#" -//- minicore: eq +//- minicore: eq, derive #[derive(Partial$0Eq)] enum Foo { Bar(String), @@ -937,7 +946,7 @@ fn add_custom_impl_partial_eq_record_enum() { check_assist( replace_derive_with_manual_impl, r#" -//- minicore: eq +//- minicore: eq, derive #[derive(Partial$0Eq)] enum Foo { Bar { @@ -981,6 +990,7 @@ fn add_custom_impl_all() { check_assist( replace_derive_with_manual_impl, r#" +//- minicore: derive mod foo { pub trait Bar { type Qux; @@ -1026,10 +1036,11 @@ fn foo() { ) } #[test] - fn add_custom_impl_for_unique_input() { + fn add_custom_impl_for_unique_input_unknown() { check_assist( replace_derive_with_manual_impl, r#" +//- minicore: derive #[derive(Debu$0g)] struct Foo { bar: String, @@ -1052,6 +1063,7 @@ fn add_custom_impl_for_with_visibility_modifier() { check_assist( replace_derive_with_manual_impl, r#" +//- minicore: derive #[derive(Debug$0)] pub struct Foo { bar: String, @@ -1074,6 +1086,7 @@ fn add_custom_impl_when_multiple_inputs() { check_assist( replace_derive_with_manual_impl, r#" +//- minicore: derive #[derive(Display, Debug$0, Serialize)] struct Foo {} "#, @@ -1093,7 +1106,7 @@ fn add_custom_impl_default_generic_record_struct() { check_assist( replace_derive_with_manual_impl, r#" -//- minicore: default +//- minicore: default, derive #[derive(Defau$0lt)] struct Foo { foo: T, @@ -1120,7 +1133,7 @@ fn add_custom_impl_clone_generic_tuple_struct_with_bounds() { check_assist( replace_derive_with_manual_impl, r#" -//- minicore: clone +//- minicore: clone, derive #[derive(Clo$0ne)] struct Foo(T, usize); "#, @@ -1141,6 +1154,7 @@ fn test_ignore_derive_macro_without_input() { check_assist_not_applicable( replace_derive_with_manual_impl, r#" +//- minicore: derive #[derive($0)] struct Foo {} "#, @@ -1152,6 +1166,7 @@ fn test_ignore_if_cursor_on_param() { check_assist_not_applicable( replace_derive_with_manual_impl, r#" +//- minicore: derive, fmt #[derive$0(Debug)] struct Foo {} "#, @@ -1160,6 +1175,7 @@ struct Foo {} check_assist_not_applicable( replace_derive_with_manual_impl, r#" +//- minicore: derive, fmt #[derive(Debug)$0] struct Foo {} "#, @@ -1171,6 +1187,7 @@ fn test_ignore_if_not_derive() { check_assist_not_applicable( replace_derive_with_manual_impl, r#" +//- minicore: derive #[allow(non_camel_$0case_types)] struct Foo {} "#, @@ -1179,10 +1196,10 @@ struct Foo {} #[test] fn works_at_start_of_file() { - cov_mark::check!(outside_of_attr_args); check_assist_not_applicable( replace_derive_with_manual_impl, r#" +//- minicore: derive, fmt $0#[derive(Debug)] struct S; "#, @@ -1194,7 +1211,7 @@ fn add_custom_impl_keep_path() { check_assist( replace_derive_with_manual_impl, r#" -//- minicore: clone +//- minicore: clone, derive #[derive(std::fmt::Debug, Clo$0ne)] pub struct Foo; "#, @@ -1216,7 +1233,7 @@ fn add_custom_impl_replace_path() { check_assist( replace_derive_with_manual_impl, r#" -//- minicore: fmt +//- minicore: fmt, derive #[derive(core::fmt::Deb$0ug, Clone)] pub struct Foo; "#, diff --git a/crates/ide_assists/src/tests/generated.rs b/crates/ide_assists/src/tests/generated.rs index 0ad4b3bc345..485b807d055 100644 --- a/crates/ide_assists/src/tests/generated.rs +++ b/crates/ide_assists/src/tests/generated.rs @@ -1766,6 +1766,7 @@ fn doctest_replace_derive_with_manual_impl() { check_doc_test( "replace_derive_with_manual_impl", r#####" +//- minicore: derive trait Debug { fn fmt(&self, f: &mut Formatter) -> Result<()>; } #[derive(Deb$0ug, Display)] struct S; diff --git a/crates/ide_db/src/helpers.rs b/crates/ide_db/src/helpers.rs index c355016c5df..6357b6c30bb 100644 --- a/crates/ide_db/src/helpers.rs +++ b/crates/ide_db/src/helpers.rs @@ -9,15 +9,14 @@ pub mod rust_doc; pub mod format_string; -use std::{collections::VecDeque, iter}; +use std::collections::VecDeque; use base_db::FileId; -use hir::{ItemInNs, MacroDef, ModuleDef, Name, PathResolution, Semantics}; +use hir::{ItemInNs, MacroDef, ModuleDef, Name, Semantics}; use itertools::Itertools; use syntax::{ ast::{self, make, HasLoopBody}, - AstNode, AstToken, Direction, SyntaxElement, SyntaxKind, SyntaxToken, TokenAtOffset, WalkEvent, - T, + AstNode, SyntaxKind, SyntaxToken, TokenAtOffset, WalkEvent, T, }; use crate::{defs::Definition, RootDatabase}; @@ -32,49 +31,6 @@ pub fn item_name(db: &RootDatabase, item: ItemInNs) -> Option { } } -/// Parses and returns the derive path at the cursor position in the given attribute, if it is a derive. -/// This special case is required because the derive macro is a compiler builtin that discards the input derives. -/// -/// The returned path is synthesized from TokenTree tokens and as such cannot be used with the [`Semantics`]. -pub fn get_path_in_derive_attr( - sema: &hir::Semantics, - attr: &ast::Attr, - cursor: &ast::Ident, -) -> Option { - let path = attr.path()?; - let tt = attr.token_tree()?; - if !tt.syntax().text_range().contains_range(cursor.syntax().text_range()) { - return None; - } - let scope = sema.scope(attr.syntax()); - let resolved_attr = sema.resolve_path(&path)?; - let derive = FamousDefs(sema, scope.krate()).core_macros_builtin_derive()?; - if PathResolution::Macro(derive) != resolved_attr { - return None; - } - get_path_at_cursor_in_tt(cursor) -} - -/// Parses the path the identifier is part of inside a token tree. -pub fn get_path_at_cursor_in_tt(cursor: &ast::Ident) -> Option { - let cursor = cursor.syntax(); - let first = cursor - .siblings_with_tokens(Direction::Prev) - .filter_map(SyntaxElement::into_token) - .take_while(|tok| tok.kind() != T!['('] && tok.kind() != T![,]) - .last()?; - let path_tokens = first - .siblings_with_tokens(Direction::Next) - .filter_map(SyntaxElement::into_token) - .take_while(|tok| tok != cursor); - - syntax::hacks::parse_expr_from_str(&path_tokens.chain(iter::once(cursor.clone())).join("")) - .and_then(|expr| match expr { - ast::Expr::PathExpr(it) => it.path(), - _ => None, - }) -} - /// Picks the token with the highest rank returned by the passed in function. pub fn pick_best_token( tokens: TokenAtOffset, diff --git a/crates/ide_db/src/helpers/import_assets.rs b/crates/ide_db/src/helpers/import_assets.rs index c037c3e0f87..319a2173529 100644 --- a/crates/ide_db/src/helpers/import_assets.rs +++ b/crates/ide_db/src/helpers/import_assets.rs @@ -8,11 +8,10 @@ use syntax::{ ast::{self, HasName}, utils::path_to_string_stripping_turbo_fish, - AstNode, AstToken, SyntaxNode, + AstNode, SyntaxNode, }; use crate::{ - helpers::get_path_in_derive_attr, items_locator::{self, AssocItemSearch, DEFAULT_QUERY_SEARCH_LIMIT}, RootDatabase, }; @@ -139,23 +138,6 @@ pub fn for_ident_pat(sema: &Semantics, pat: &ast::IdentPat) -> Opt }) } - pub fn for_derive_ident(sema: &Semantics, ident: &ast::Ident) -> Option { - let attr = ident.syntax().ancestors().find_map(ast::Attr::cast)?; - let path = get_path_in_derive_attr(sema, &attr, ident)?; - - if let Some(_) = path.qualifier() { - return None; - } - - let name = NameToImport::exact_case_sensitive(path.segment()?.name_ref()?.to_string()); - let candidate_node = attr.syntax().clone(); - Some(Self { - import_candidate: ImportCandidate::Path(PathImportCandidate { qualifier: None, name }), - module_with_candidate: sema.scope(&candidate_node).module()?, - candidate_node, - }) - } - pub fn for_fuzzy_path( module_with_candidate: Module, qualifier: Option, diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs index 5ff6519c9cc..1b916e91bfd 100644 --- a/crates/syntax/src/ast/node_ext.rs +++ b/crates/syntax/src/ast/node_ext.rs @@ -705,6 +705,15 @@ pub fn end(&self) -> Option { } impl ast::TokenTree { + pub fn token_trees_and_tokens( + &self, + ) -> impl Iterator> { + self.syntax().children_with_tokens().filter_map(|not| match not { + NodeOrToken::Node(node) => ast::TokenTree::cast(node).map(NodeOrToken::Node), + NodeOrToken::Token(t) => Some(NodeOrToken::Token(t)), + }) + } + pub fn left_delimiter_token(&self) -> Option { self.syntax() .first_child_or_token()?