From a193666361f6ea9725b927a35f5baf77da713c0a Mon Sep 17 00:00:00 2001 From: Edwin Cheng Date: Sat, 27 Mar 2021 13:44:54 +0800 Subject: Basic Support Macro 2.0 --- crates/hir_def/src/nameres/collector.rs | 102 ++++++++++++++++++----------- crates/hir_def/src/nameres/tests/macros.rs | 22 +++++++ crates/hir_expand/src/db.rs | 60 +++++++++++------ crates/hir_expand/src/hygiene.rs | 2 +- crates/hir_expand/src/lib.rs | 2 +- crates/hir_ty/src/tests/macros.rs | 83 ++++++++++++++++++++++- crates/syntax/src/ast/node_ext.rs | 1 + 7 files changed, 209 insertions(+), 63 deletions(-) diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs index d8fabe49b..d58135ec9 100644 --- a/crates/hir_def/src/nameres/collector.rs +++ b/crates/hir_def/src/nameres/collector.rs @@ -25,8 +25,8 @@ use crate::{ derive_macro_as_call_id, item_scope::{ImportType, PerNsGlobImports}, item_tree::{ - self, FileItemTreeId, ItemTree, ItemTreeId, MacroCall, MacroRules, Mod, ModItem, ModKind, - StructDefKind, + self, FileItemTreeId, ItemTree, ItemTreeId, MacroCall, MacroDef, MacroRules, Mod, ModItem, + ModKind, StructDefKind, }, macro_call_as_call_id, nameres::{ @@ -395,7 +395,7 @@ impl DefCollector<'_> { /// macro_rules! foo { () => {} } /// use foo as bar; /// ``` - fn define_macro( + fn define_macro_rules( &mut self, module_id: LocalModuleId, name: Name, @@ -430,6 +430,21 @@ impl DefCollector<'_> { self.def_map.modules[module_id].scope.define_legacy_macro(name, mac); } + /// Define a macro 2.0 macro + /// + /// The scoped of macro 2.0 macro is equal to normal function + fn define_macro_def( + &mut self, + module_id: LocalModuleId, + name: Name, + macro_: MacroDefId, + vis: &RawVisibility, + ) { + let vis = + self.def_map.resolve_visibility(self.db, module_id, vis).unwrap_or(Visibility::Public); + self.update(module_id, &[(Some(name), PerNs::macros(macro_, vis))], vis, ImportType::Named); + } + /// Define a proc macro /// /// A proc macro is similar to normal macro scope, but it would not visible in legacy textual scoped. @@ -1067,40 +1082,7 @@ impl ModCollector<'_, '_> { } ModItem::MacroCall(mac) => self.collect_macro_call(&self.item_tree[mac]), ModItem::MacroRules(id) => self.collect_macro_rules(id), - ModItem::MacroDef(id) => { - let mac = &self.item_tree[id]; - let ast_id = InFile::new(self.file_id, mac.ast_id.upcast()); - - // "Macro 2.0" is not currently supported by rust-analyzer, but libcore uses it - // to define builtin macros, so we support at least that part. - let attrs = self.item_tree.attrs( - self.def_collector.db, - krate, - ModItem::from(id).into(), - ); - if attrs.by_key("rustc_builtin_macro").exists() { - let krate = self.def_collector.def_map.krate; - let macro_id = find_builtin_macro(&mac.name, krate, ast_id) - .or_else(|| find_builtin_derive(&mac.name, krate, ast_id)); - if let Some(macro_id) = macro_id { - let vis = self - .def_collector - .def_map - .resolve_visibility( - self.def_collector.db, - self.module_id, - &self.item_tree[mac.visibility], - ) - .unwrap_or(Visibility::Public); - self.def_collector.update( - self.module_id, - &[(Some(mac.name.clone()), PerNs::macros(macro_id, vis))], - vis, - ImportType::Named, - ); - } - } - } + ModItem::MacroDef(id) => self.collect_macro_def(id), ModItem::Impl(imp) => { let module = self.def_collector.def_map.module_id(self.module_id); let impl_id = @@ -1420,7 +1402,7 @@ impl ModCollector<'_, '_> { if attrs.by_key("rustc_builtin_macro").exists() { let krate = self.def_collector.def_map.krate; if let Some(macro_id) = find_builtin_macro(&mac.name, krate, ast_id) { - self.def_collector.define_macro( + self.def_collector.define_macro_rules( self.module_id, mac.name.clone(), macro_id, @@ -1436,7 +1418,49 @@ impl ModCollector<'_, '_> { kind: MacroDefKind::Declarative(ast_id), local_inner: is_local_inner, }; - self.def_collector.define_macro(self.module_id, mac.name.clone(), macro_id, is_export); + self.def_collector.define_macro_rules( + self.module_id, + mac.name.clone(), + macro_id, + is_export, + ); + } + + fn collect_macro_def(&mut self, id: FileItemTreeId) { + let krate = self.def_collector.def_map.krate; + let mac = &self.item_tree[id]; + let ast_id = InFile::new(self.file_id, mac.ast_id.upcast()); + + // Case 1: bulitin macros + let attrs = self.item_tree.attrs(self.def_collector.db, krate, ModItem::from(id).into()); + if attrs.by_key("rustc_builtin_macro").exists() { + let macro_id = find_builtin_macro(&mac.name, krate, ast_id) + .or_else(|| find_builtin_derive(&mac.name, krate, ast_id)); + + if let Some(macro_id) = macro_id { + self.def_collector.define_macro_def( + self.module_id, + mac.name.clone(), + macro_id, + &self.item_tree[mac.visibility], + ); + } + return; + } + + // Case 2: normal `macro` + let macro_id = MacroDefId { + krate: self.def_collector.def_map.krate, + kind: MacroDefKind::Declarative(ast_id), + local_inner: false, + }; + + self.def_collector.define_macro_def( + self.module_id, + mac.name.clone(), + macro_id, + &self.item_tree[mac.visibility], + ); } fn collect_macro_call(&mut self, mac: &MacroCall) { diff --git a/crates/hir_def/src/nameres/tests/macros.rs b/crates/hir_def/src/nameres/tests/macros.rs index 6d3cb8d7a..9986e99e4 100644 --- a/crates/hir_def/src/nameres/tests/macros.rs +++ b/crates/hir_def/src/nameres/tests/macros.rs @@ -837,3 +837,25 @@ fn collects_derive_helpers() { _ => unreachable!(), } } + +#[test] +fn resolve_macro_def() { + check( + r#" +//- /lib.rs +pub macro structs($($i:ident),*) { + $(struct $i { field: u32 } )* +} + +structs!(Foo); + +//- /nested.rs +structs!(Bar, Baz); +"#, + expect![[r#" + crate + Foo: t + structs: m + "#]], + ); +} diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs index d672f6723..c0ab70b60 100644 --- a/crates/hir_expand/src/db.rs +++ b/crates/hir_expand/src/db.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use base_db::{salsa, SourceDatabase}; -use mbe::{ExpandError, ExpandResult, MacroRules}; +use mbe::{ExpandError, ExpandResult, MacroDef, MacroRules}; use parser::FragmentKind; use syntax::{ algo::diff, @@ -28,6 +28,7 @@ const TOKEN_LIMIT: usize = 524288; #[derive(Debug, Clone, Eq, PartialEq)] pub enum TokenExpander { MacroRules(mbe::MacroRules), + MacroDef(mbe::MacroDef), Builtin(BuiltinFnLikeExpander), BuiltinDerive(BuiltinDeriveExpander), ProcMacro(ProcMacroExpander), @@ -42,6 +43,7 @@ impl TokenExpander { ) -> mbe::ExpandResult { match self { TokenExpander::MacroRules(it) => it.expand(tt), + TokenExpander::MacroDef(it) => it.expand(tt), TokenExpander::Builtin(it) => it.expand(db, id, tt), // FIXME switch these to ExpandResult as well TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt).into(), @@ -57,6 +59,7 @@ impl TokenExpander { pub fn map_id_down(&self, id: tt::TokenId) -> tt::TokenId { match self { TokenExpander::MacroRules(it) => it.map_id_down(id), + TokenExpander::MacroDef(it) => it.map_id_down(id), TokenExpander::Builtin(..) => id, TokenExpander::BuiltinDerive(..) => id, TokenExpander::ProcMacro(..) => id, @@ -66,6 +69,7 @@ impl TokenExpander { pub fn map_id_up(&self, id: tt::TokenId) -> (tt::TokenId, mbe::Origin) { match self { TokenExpander::MacroRules(it) => it.map_id_up(id), + TokenExpander::MacroDef(it) => it.map_id_up(id), TokenExpander::Builtin(..) => (id, mbe::Origin::Call), TokenExpander::BuiltinDerive(..) => (id, mbe::Origin::Call), TokenExpander::ProcMacro(..) => (id, mbe::Origin::Call), @@ -136,26 +140,40 @@ fn ast_id_map(db: &dyn AstDatabase, file_id: HirFileId) -> Arc { fn macro_def(db: &dyn AstDatabase, id: MacroDefId) -> Option> { match id.kind { - MacroDefKind::Declarative(ast_id) => { - let macro_rules = match ast_id.to_node(db) { - syntax::ast::Macro::MacroRules(mac) => mac, - syntax::ast::Macro::MacroDef(_) => return None, - }; - let arg = macro_rules.token_tree()?; - let (tt, tmap) = mbe::ast_to_token_tree(&arg).or_else(|| { - log::warn!("fail on macro_def to token tree: {:#?}", arg); - None - })?; - let rules = match MacroRules::parse(&tt) { - Ok(it) => it, - Err(err) => { - let name = macro_rules.name().map(|n| n.to_string()).unwrap_or_default(); - log::warn!("fail on macro_def parse ({}): {:?} {:#?}", name, err, tt); - return None; - } - }; - Some(Arc::new((TokenExpander::MacroRules(rules), tmap))) - } + MacroDefKind::Declarative(ast_id) => match ast_id.to_node(db) { + syntax::ast::Macro::MacroRules(macro_rules) => { + let arg = macro_rules.token_tree()?; + let (tt, tmap) = mbe::ast_to_token_tree(&arg).or_else(|| { + log::warn!("fail on macro_rules to token tree: {:#?}", arg); + None + })?; + let rules = match MacroRules::parse(&tt) { + Ok(it) => it, + Err(err) => { + let name = macro_rules.name().map(|n| n.to_string()).unwrap_or_default(); + log::warn!("fail on macro_def parse ({}): {:?} {:#?}", name, err, tt); + return None; + } + }; + Some(Arc::new((TokenExpander::MacroRules(rules), tmap))) + } + syntax::ast::Macro::MacroDef(macro_def) => { + let arg = macro_def.body()?; + let (tt, tmap) = mbe::ast_to_token_tree(&arg).or_else(|| { + log::warn!("fail on macro_def to token tree: {:#?}", arg); + None + })?; + let rules = match MacroDef::parse(&tt) { + Ok(it) => it, + Err(err) => { + let name = macro_def.name().map(|n| n.to_string()).unwrap_or_default(); + log::warn!("fail on macro_def parse ({}): {:?} {:#?}", name, err, tt); + return None; + } + }; + Some(Arc::new((TokenExpander::MacroDef(rules), tmap))) + } + }, MacroDefKind::BuiltIn(expander, _) => { Some(Arc::new((TokenExpander::Builtin(expander), mbe::TokenMap::default()))) } diff --git a/crates/hir_expand/src/hygiene.rs b/crates/hir_expand/src/hygiene.rs index 0e0f7214a..779725629 100644 --- a/crates/hir_expand/src/hygiene.rs +++ b/crates/hir_expand/src/hygiene.rs @@ -148,7 +148,7 @@ fn make_hygiene_info( let def_offset = loc.def.ast_id().left().and_then(|id| { let def_tt = match id.to_node(db) { ast::Macro::MacroRules(mac) => mac.token_tree()?.syntax().text_range().start(), - ast::Macro::MacroDef(_) => return None, + ast::Macro::MacroDef(mac) => mac.body()?.syntax().text_range().start(), }; Some(InFile::new(id.file_id, def_tt)) }); diff --git a/crates/hir_expand/src/lib.rs b/crates/hir_expand/src/lib.rs index b8045fda9..3e332ee47 100644 --- a/crates/hir_expand/src/lib.rs +++ b/crates/hir_expand/src/lib.rs @@ -151,7 +151,7 @@ impl HirFileId { let def = loc.def.ast_id().left().and_then(|id| { let def_tt = match id.to_node(db) { ast::Macro::MacroRules(mac) => mac.token_tree()?, - ast::Macro::MacroDef(_) => return None, + ast::Macro::MacroDef(mac) => mac.body()?, }; Some(InFile::new(id.file_id, def_tt)) }); diff --git a/crates/hir_ty/src/tests/macros.rs b/crates/hir_ty/src/tests/macros.rs index 01935ec99..3eb01dbd0 100644 --- a/crates/hir_ty/src/tests/macros.rs +++ b/crates/hir_ty/src/tests/macros.rs @@ -135,7 +135,88 @@ fn infer_path_qualified_macros_expanded() { } #[test] -fn expr_macro_expanded_in_various_places() { +fn expr_macro_def_expanded_in_various_places() { + check_infer( + r#" + macro spam() { + 1isize + } + + fn spam() { + spam!(); + (spam!()); + spam!().spam(spam!()); + for _ in spam!() {} + || spam!(); + while spam!() {} + break spam!(); + return spam!(); + match spam!() { + _ if spam!() => spam!(), + } + spam!()(spam!()); + Spam { spam: spam!() }; + spam!()[spam!()]; + await spam!(); + spam!() as usize; + &spam!(); + -spam!(); + spam!()..spam!(); + spam!() + spam!(); + } + "#, + expect![[r#" + !0..6 '1isize': isize + !0..6 '1isize': isize + !0..6 '1isize': isize + !0..6 '1isize': isize + !0..6 '1isize': isize + !0..6 '1isize': isize + !0..6 '1isize': isize + !0..6 '1isize': isize + !0..6 '1isize': isize + !0..6 '1isize': isize + !0..6 '1isize': isize + !0..6 '1isize': isize + !0..6 '1isize': isize + !0..6 '1isize': isize + !0..6 '1isize': isize + !0..6 '1isize': isize + !0..6 '1isize': isize + !0..6 '1isize': isize + !0..6 '1isize': isize + !0..6 '1isize': isize + !0..6 '1isize': isize + !0..6 '1isize': isize + !0..6 '1isize': isize + !0..6 '1isize': isize + !0..6 '1isize': isize + 39..442 '{ ...!(); }': () + 73..94 'spam!(...am!())': {unknown} + 100..119 'for _ ...!() {}': () + 104..105 '_': {unknown} + 117..119 '{}': () + 124..134 '|| spam!()': || -> isize + 140..156 'while ...!() {}': () + 154..156 '{}': () + 161..174 'break spam!()': ! + 180..194 'return spam!()': ! + 200..254 'match ... }': isize + 224..225 '_': isize + 259..275 'spam!(...am!())': {unknown} + 281..303 'Spam {...m!() }': {unknown} + 309..325 'spam!(...am!()]': {unknown} + 350..366 'spam!(... usize': usize + 372..380 '&spam!()': &isize + 386..394 '-spam!()': isize + 400..416 'spam!(...pam!()': {unknown} + 422..439 'spam!(...pam!()': isize + "#]], + ); +} + +#[test] +fn expr_macro_rules_expanded_in_various_places() { check_infer( r#" macro_rules! spam { diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs index 2772d7364..ae98dbd26 100644 --- a/crates/syntax/src/ast/node_ext.rs +++ b/crates/syntax/src/ast/node_ext.rs @@ -40,6 +40,7 @@ fn text_of_first_token(node: &SyntaxNode) -> TokenText { TokenText(first_token) } +#[derive(Debug, PartialEq, Eq, Clone)] pub enum Macro { MacroRules(ast::MacroRules), MacroDef(ast::MacroDef), -- cgit v1.2.3 From 4520002b63b5a27e7676822aecfb2d435bf36e5a Mon Sep 17 00:00:00 2001 From: Edwin Cheng Date: Sat, 27 Mar 2021 13:48:15 +0800 Subject: Unleash macro 2.0 in hightlight and more --- crates/hir/src/semantics.rs | 2 +- crates/hir/src/semantics/source_to_def.rs | 5 +- crates/ide/src/doc_links.rs | 2 +- crates/ide/src/file_structure.rs | 2 +- crates/ide/src/move_item.rs | 1 + crates/ide/src/syntax_highlighting.rs | 28 ++--- crates/ide/src/syntax_highlighting/inject.rs | 3 +- crates/ide/src/syntax_highlighting/macro_.rs | 129 +++++++++++++++++++++ crates/ide/src/syntax_highlighting/macro_rules.rs | 129 --------------------- .../test_data/highlighting.html | 12 +- crates/ide/src/syntax_highlighting/tests.rs | 10 ++ crates/ide_db/src/defs.rs | 2 +- crates/ide_db/src/symbol_index.rs | 3 +- 13 files changed, 173 insertions(+), 155 deletions(-) create mode 100644 crates/ide/src/syntax_highlighting/macro_.rs delete mode 100644 crates/ide/src/syntax_highlighting/macro_rules.rs diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index 3ff135f41..d3caeef4e 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -768,7 +768,7 @@ to_def_impls![ (crate::TypeParam, ast::TypeParam, type_param_to_def), (crate::LifetimeParam, ast::LifetimeParam, lifetime_param_to_def), (crate::ConstParam, ast::ConstParam, const_param_to_def), - (crate::MacroDef, ast::MacroRules, macro_rules_to_def), + (crate::MacroDef, ast::Macro, macro_to_def), (crate::Local, ast::IdentPat, bind_pat_to_def), (crate::Local, ast::SelfParam, self_param_to_def), (crate::Label, ast::Label, label_to_def), diff --git a/crates/hir/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs index 762809fcd..9a5a2255f 100644 --- a/crates/hir/src/semantics/source_to_def.rs +++ b/crates/hir/src/semantics/source_to_def.rs @@ -191,10 +191,7 @@ impl SourceToDefCtx<'_, '_> { } // FIXME: use DynMap as well? - pub(super) fn macro_rules_to_def( - &mut self, - src: InFile, - ) -> Option { + pub(super) fn macro_to_def(&mut self, src: InFile) -> Option { let file_ast_id = self.db.ast_id_map(src.file_id).ast_id(&src.value); let ast_id = AstId::new(src.file_id, file_ast_id.upcast()); let kind = MacroDefKind::Declarative(ast_id); diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs index 0cee741ac..9301cdeff 100644 --- a/crates/ide/src/doc_links.rs +++ b/crates/ide/src/doc_links.rs @@ -161,7 +161,7 @@ pub(crate) fn doc_owner_to_def( ast::Variant(it) => sema.to_def(&it)?.into(), ast::Trait(it) => sema.to_def(&it)?.into(), ast::Impl(it) => return sema.to_def(&it).map(Definition::SelfType), - ast::MacroRules(it) => return sema.to_def(&it).map(Definition::Macro), + ast::Macro(it) => return sema.to_def(&it).map(Definition::Macro), ast::TupleField(it) => return sema.to_def(&it).map(Definition::Field), ast::RecordField(it) => return sema.to_def(&it).map(Definition::Field), _ => return None, diff --git a/crates/ide/src/file_structure.rs b/crates/ide/src/file_structure.rs index 9f879a66e..2c898eae8 100644 --- a/crates/ide/src/file_structure.rs +++ b/crates/ide/src/file_structure.rs @@ -172,7 +172,7 @@ fn structure_node(node: &SyntaxNode) -> Option { }; Some(node) }, - ast::MacroRules(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Macro)), + ast::Macro(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Macro)), _ => None, } } diff --git a/crates/ide/src/move_item.rs b/crates/ide/src/move_item.rs index 48690b073..05fa8fc13 100644 --- a/crates/ide/src/move_item.rs +++ b/crates/ide/src/move_item.rs @@ -66,6 +66,7 @@ fn find_ancestors(item: SyntaxElement, direction: Direction, range: TextRange) - SyntaxKind::STATIC, SyntaxKind::CONST, SyntaxKind::MACRO_RULES, + SyntaxKind::MACRO_DEF, ]; let ancestor = once(root.clone()) diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs index e25b698e0..67a10766b 100644 --- a/crates/ide/src/syntax_highlighting.rs +++ b/crates/ide/src/syntax_highlighting.rs @@ -5,7 +5,7 @@ mod injector; mod highlight; mod format; -mod macro_rules; +mod macro_; mod inject; mod html; @@ -24,8 +24,8 @@ use syntax::{ use crate::{ syntax_highlighting::{ - format::highlight_format_string, highlights::Highlights, - macro_rules::MacroRulesHighlighter, tags::Highlight, + format::highlight_format_string, highlights::Highlights, macro_::MacroHighlighter, + tags::Highlight, }, FileId, HlMod, HlTag, }; @@ -93,8 +93,8 @@ fn traverse( let mut bindings_shadow_count: FxHashMap = FxHashMap::default(); let mut current_macro_call: Option = None; - let mut current_macro_rules: Option = None; - let mut macro_rules_highlighter = MacroRulesHighlighter::default(); + let mut current_macro: Option = None; + let mut macro_highlighter = MacroHighlighter::default(); let mut inside_attribute = false; // Walk all nodes, keeping track of whether we are inside a macro or not. @@ -129,16 +129,16 @@ fn traverse( _ => (), } - match event.clone().map(|it| it.into_node().and_then(ast::MacroRules::cast)) { + match event.clone().map(|it| it.into_node().and_then(ast::Macro::cast)) { WalkEvent::Enter(Some(mac)) => { - macro_rules_highlighter.init(); - current_macro_rules = Some(mac); + macro_highlighter.init(); + current_macro = Some(mac); continue; } WalkEvent::Leave(Some(mac)) => { - assert_eq!(current_macro_rules, Some(mac)); - current_macro_rules = None; - macro_rules_highlighter = MacroRulesHighlighter::default(); + assert_eq!(current_macro, Some(mac)); + current_macro = None; + macro_highlighter = MacroHighlighter::default(); } _ => (), } @@ -164,9 +164,9 @@ fn traverse( let range = element.text_range(); - if current_macro_rules.is_some() { + if current_macro.is_some() { if let Some(tok) = element.as_token() { - macro_rules_highlighter.advance(tok); + macro_highlighter.advance(tok); } } @@ -200,7 +200,7 @@ fn traverse( } } - if let Some(_) = macro_rules_highlighter.highlight(element_to_highlight.clone()) { + if let Some(_) = macro_highlighter.highlight(element_to_highlight.clone()) { continue; } diff --git a/crates/ide/src/syntax_highlighting/inject.rs b/crates/ide/src/syntax_highlighting/inject.rs index 38bf49348..963c3fb59 100644 --- a/crates/ide/src/syntax_highlighting/inject.rs +++ b/crates/ide/src/syntax_highlighting/inject.rs @@ -109,8 +109,7 @@ fn doc_attributes<'node>( ast::Impl(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::SelfType(def))), ast::RecordField(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Field(def))), ast::TupleField(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Field(def))), - ast::MacroRules(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Macro(def))), - // ast::MacroDef(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))), + ast::Macro(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Macro(def))), // ast::Use(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))), _ => return None } diff --git a/crates/ide/src/syntax_highlighting/macro_.rs b/crates/ide/src/syntax_highlighting/macro_.rs new file mode 100644 index 000000000..819704294 --- /dev/null +++ b/crates/ide/src/syntax_highlighting/macro_.rs @@ -0,0 +1,129 @@ +//! Syntax highlighting for macro_rules!. +use syntax::{SyntaxElement, SyntaxKind, SyntaxToken, TextRange, T}; + +use crate::{HlRange, HlTag}; + +#[derive(Default)] +pub(super) struct MacroHighlighter { + state: Option, +} + +impl MacroHighlighter { + pub(super) fn init(&mut self) { + self.state = Some(MacroMatcherParseState::default()); + } + + pub(super) fn advance(&mut self, token: &SyntaxToken) { + if let Some(state) = self.state.as_mut() { + update_macro_state(state, token); + } + } + + pub(super) fn highlight(&self, element: SyntaxElement) -> Option { + if let Some(state) = self.state.as_ref() { + if matches!(state.rule_state, RuleState::Matcher | RuleState::Expander) { + if let Some(range) = is_metavariable(element) { + return Some(HlRange { + range, + highlight: HlTag::UnresolvedReference.into(), + binding_hash: None, + }); + } + } + } + None + } +} + +struct MacroMatcherParseState { + /// Opening and corresponding closing bracket of the matcher or expander of the current rule + paren_ty: Option<(SyntaxKind, SyntaxKind)>, + paren_level: usize, + rule_state: RuleState, + /// Whether we are inside the outer `{` `}` macro block that holds the rules + in_invoc_body: bool, +} + +impl Default for MacroMatcherParseState { + fn default() -> Self { + MacroMatcherParseState { + paren_ty: None, + paren_level: 0, + in_invoc_body: false, + rule_state: RuleState::None, + } + } +} + +#[derive(Copy, Clone, Debug, PartialEq)] +enum RuleState { + Matcher, + Expander, + Between, + None, +} + +impl RuleState { + fn transition(&mut self) { + *self = match self { + RuleState::Matcher => RuleState::Between, + RuleState::Expander => RuleState::None, + RuleState::Between => RuleState::Expander, + RuleState::None => RuleState::Matcher, + }; + } +} + +fn update_macro_state(state: &mut MacroMatcherParseState, tok: &SyntaxToken) { + if !state.in_invoc_body { + if tok.kind() == T!['{'] || tok.kind() == T!['('] { + state.in_invoc_body = true; + } + return; + } + + match state.paren_ty { + Some((open, close)) => { + if tok.kind() == open { + state.paren_level += 1; + } else if tok.kind() == close { + state.paren_level -= 1; + if state.paren_level == 0 { + state.rule_state.transition(); + state.paren_ty = None; + } + } + } + None => { + match tok.kind() { + T!['('] => { + state.paren_ty = Some((T!['('], T![')'])); + } + T!['{'] => { + state.paren_ty = Some((T!['{'], T!['}'])); + } + T!['['] => { + state.paren_ty = Some((T!['['], T![']'])); + } + _ => (), + } + if state.paren_ty.is_some() { + state.paren_level = 1; + state.rule_state.transition(); + } + } + } +} + +fn is_metavariable(element: SyntaxElement) -> Option { + let tok = element.as_token()?; + match tok.kind() { + kind if kind == SyntaxKind::IDENT || kind.is_keyword() => { + if let Some(_dollar) = tok.prev_token().filter(|t| t.kind() == T![$]) { + return Some(tok.text_range()); + } + } + _ => (), + }; + None +} diff --git a/crates/ide/src/syntax_highlighting/macro_rules.rs b/crates/ide/src/syntax_highlighting/macro_rules.rs deleted file mode 100644 index 44620e912..000000000 --- a/crates/ide/src/syntax_highlighting/macro_rules.rs +++ /dev/null @@ -1,129 +0,0 @@ -//! Syntax highlighting for macro_rules!. -use syntax::{SyntaxElement, SyntaxKind, SyntaxToken, TextRange, T}; - -use crate::{HlRange, HlTag}; - -#[derive(Default)] -pub(super) struct MacroRulesHighlighter { - state: Option, -} - -impl MacroRulesHighlighter { - pub(super) fn init(&mut self) { - self.state = Some(MacroMatcherParseState::default()); - } - - pub(super) fn advance(&mut self, token: &SyntaxToken) { - if let Some(state) = self.state.as_mut() { - update_macro_rules_state(state, token); - } - } - - pub(super) fn highlight(&self, element: SyntaxElement) -> Option { - if let Some(state) = self.state.as_ref() { - if matches!(state.rule_state, RuleState::Matcher | RuleState::Expander) { - if let Some(range) = is_metavariable(element) { - return Some(HlRange { - range, - highlight: HlTag::UnresolvedReference.into(), - binding_hash: None, - }); - } - } - } - None - } -} - -struct MacroMatcherParseState { - /// Opening and corresponding closing bracket of the matcher or expander of the current rule - paren_ty: Option<(SyntaxKind, SyntaxKind)>, - paren_level: usize, - rule_state: RuleState, - /// Whether we are inside the outer `{` `}` macro block that holds the rules - in_invoc_body: bool, -} - -impl Default for MacroMatcherParseState { - fn default() -> Self { - MacroMatcherParseState { - paren_ty: None, - paren_level: 0, - in_invoc_body: false, - rule_state: RuleState::None, - } - } -} - -#[derive(Copy, Clone, Debug, PartialEq)] -enum RuleState { - Matcher, - Expander, - Between, - None, -} - -impl RuleState { - fn transition(&mut self) { - *self = match self { - RuleState::Matcher => RuleState::Between, - RuleState::Expander => RuleState::None, - RuleState::Between => RuleState::Expander, - RuleState::None => RuleState::Matcher, - }; - } -} - -fn update_macro_rules_state(state: &mut MacroMatcherParseState, tok: &SyntaxToken) { - if !state.in_invoc_body { - if tok.kind() == T!['{'] { - state.in_invoc_body = true; - } - return; - } - - match state.paren_ty { - Some((open, close)) => { - if tok.kind() == open { - state.paren_level += 1; - } else if tok.kind() == close { - state.paren_level -= 1; - if state.paren_level == 0 { - state.rule_state.transition(); - state.paren_ty = None; - } - } - } - None => { - match tok.kind() { - T!['('] => { - state.paren_ty = Some((T!['('], T![')'])); - } - T!['{'] => { - state.paren_ty = Some((T!['{'], T!['}'])); - } - T!['['] => { - state.paren_ty = Some((T!['['], T![']'])); - } - _ => (), - } - if state.paren_ty.is_some() { - state.paren_level = 1; - state.rule_state.transition(); - } - } - } -} - -fn is_metavariable(element: SyntaxElement) -> Option { - let tok = element.as_token()?; - match tok.kind() { - kind if kind == SyntaxKind::IDENT || kind.is_keyword() => { - if let Some(_dollar) = tok.prev_token().filter(|t| t.kind() == T![$]) { - return Some(tok.text_range()); - } - } - _ => (), - }; - None -} diff --git a/crates/ide/src/syntax_highlighting/test_data/highlighting.html b/crates/ide/src/syntax_highlighting/test_data/highlighting.html index 8b2dd3b70..1eaa7b75b 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlighting.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlighting.html @@ -41,7 +41,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd mod inner {} #[rustc_builtin_macro] -macro Copy {} +macro Copy {} // Needed for function consuming vs normal pub mod marker { @@ -158,6 +158,16 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd ($type:ty) => ($type) } +macro with_args($i:ident) { + $i +} + +macro without_args { + ($i:ident) => { + $i + } +} + // comment fn main() { println!("Hello, {}!", 92); diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs index 7b2922b0d..369ae0972 100644 --- a/crates/ide/src/syntax_highlighting/tests.rs +++ b/crates/ide/src/syntax_highlighting/tests.rs @@ -129,6 +129,16 @@ macro_rules! keyword_frag { ($type:ty) => ($type) } +macro with_args($i:ident) { + $i +} + +macro without_args { + ($i:ident) => { + $i + } +} + // comment fn main() { println!("Hello, {}!", 92); diff --git a/crates/ide_db/src/defs.rs b/crates/ide_db/src/defs.rs index ab23dd7ac..0d9808d24 100644 --- a/crates/ide_db/src/defs.rs +++ b/crates/ide_db/src/defs.rs @@ -227,7 +227,7 @@ impl NameClass { let def: hir::TypeAlias = sema.to_def(&it)?; Some(NameClass::Definition(Definition::ModuleDef(def.into()))) }, - ast::MacroRules(it) => { + ast::Macro(it) => { let def = sema.to_def(&it)?; Some(NameClass::Definition(Definition::Macro(def))) }, diff --git a/crates/ide_db/src/symbol_index.rs b/crates/ide_db/src/symbol_index.rs index 35e382b5c..da427d686 100644 --- a/crates/ide_db/src/symbol_index.rs +++ b/crates/ide_db/src/symbol_index.rs @@ -438,7 +438,7 @@ fn to_symbol(node: &SyntaxNode) -> Option<(SmolStr, SyntaxNodePtr, TextRange)> { ast::TypeAlias(it) => decl(it), ast::Const(it) => decl(it), ast::Static(it) => decl(it), - ast::MacroRules(it) => decl(it), + ast::Macro(it) => decl(it), ast::Union(it) => decl(it), _ => None, } @@ -458,6 +458,7 @@ fn to_file_symbol(node: &SyntaxNode, file_id: FileId) -> Option { CONST => FileSymbolKind::Const, STATIC => FileSymbolKind::Static, MACRO_RULES => FileSymbolKind::Macro, + MACRO_DEF => FileSymbolKind::Macro, UNION => FileSymbolKind::Union, kind => unreachable!("{:?}", kind), }, -- cgit v1.2.3 From eedadd70241e70f76b178763fd0e005217e36ea1 Mon Sep 17 00:00:00 2001 From: Edwin Cheng Date: Sat, 27 Mar 2021 14:47:04 +0800 Subject: Add support for doc on hover for macro 2.0 --- crates/ide/src/hover.rs | 31 +++++++++++++++++++++++++++++ crates/syntax/src/parsing/text_tree_sink.rs | 4 ++-- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index c43089476..3c951c507 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs @@ -1234,6 +1234,37 @@ fn f() { fo$0o!(); } ) } + #[test] + fn test_hover_macro2_invocation() { + check( + r#" +/// foo bar +/// +/// foo bar baz +macro foo() {} + +fn f() { fo$0o!(); } +"#, + expect![[r#" + *foo* + + ```rust + test + ``` + + ```rust + macro foo + ``` + + --- + + foo bar + + foo bar baz + "#]], + ) + } + #[test] fn test_hover_tuple_field() { check( diff --git a/crates/syntax/src/parsing/text_tree_sink.rs b/crates/syntax/src/parsing/text_tree_sink.rs index bb10f20c9..1934204ea 100644 --- a/crates/syntax/src/parsing/text_tree_sink.rs +++ b/crates/syntax/src/parsing/text_tree_sink.rs @@ -147,8 +147,8 @@ fn n_attached_trivias<'a>( trivias: impl Iterator, ) -> usize { match kind { - MACRO_CALL | MACRO_RULES | CONST | TYPE_ALIAS | STRUCT | UNION | ENUM | VARIANT | FN - | TRAIT | MODULE | RECORD_FIELD | STATIC | USE => { + MACRO_CALL | MACRO_RULES | MACRO_DEF | CONST | TYPE_ALIAS | STRUCT | UNION | ENUM + | VARIANT | FN | TRAIT | MODULE | RECORD_FIELD | STATIC | USE => { let mut res = 0; let mut trivias = trivias.enumerate().peekable(); -- cgit v1.2.3 From 772987911851d6480ec8c905c0cac1e2f881152c Mon Sep 17 00:00:00 2001 From: Edwin Cheng Date: Sun, 28 Mar 2021 10:41:18 +0800 Subject: Remove unused test fixtures --- crates/hir_def/src/nameres/tests/macros.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/crates/hir_def/src/nameres/tests/macros.rs b/crates/hir_def/src/nameres/tests/macros.rs index 9986e99e4..c37f915ab 100644 --- a/crates/hir_def/src/nameres/tests/macros.rs +++ b/crates/hir_def/src/nameres/tests/macros.rs @@ -846,11 +846,7 @@ fn resolve_macro_def() { pub macro structs($($i:ident),*) { $(struct $i { field: u32 } )* } - structs!(Foo); - -//- /nested.rs -structs!(Bar, Baz); "#, expect![[r#" crate -- cgit v1.2.3