diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2019-12-05 20:00:20 +0000 |
---|---|---|
committer | GitHub <[email protected]> | 2019-12-05 20:00:20 +0000 |
commit | 6e10a9f57815ad865a570816436adfdf0de1cdf0 (patch) | |
tree | 410c416bfe9daa05743ce8c49418c20df28dd625 /crates/ra_hir_def/src | |
parent | 217a6fa4a387dbfe6ac725b6dba2f15d6532679f (diff) | |
parent | 10697041c1c72ddbe27c41912e691656be6ccce4 (diff) |
Merge #2479
2479: Add expansion infrastructure for derive macros r=matklad a=flodiebold
I thought I'd experiment a bit with attribute macro/derive expansion, and here's what I've got so far. It has dummy implementations of the Copy / Clone derives, to show that the approach works; it doesn't add any attribute macro support, but I think that fits into the architecture.
Basically, during raw item collection, we look at the attributes and generate macro calls for them if necessary. Currently I only do this for derives, and just add the derive macro calls as separate calls next to the item. I think for derives, it's important that they don't obscure the actual item, since they can't actually change it (e.g. sending the item token tree through macro expansion unnecessarily might make completion within it more complicated).
Attribute macros would have to be recognized at that stage and replace the item (i.e., the raw item collector will just emit an attribute macro call, and not the item). I think when we implement this, we should try to recognize known inert attributes, so that we don't do macro expansion unnecessarily; anything that isn't known needs to be treated as a possible attribute macro call (since the raw item collector can't resolve the macro yet).
There's basically no name resolution for attribute macros implemented, I just hardcoded the built-in derives. In the future, the built-ins should work within the normal name resolution infrastructure; the problem there is that the builtin stubs in `std` use macros 2.0, which we don't support yet (and adding support is outside the scope of this).
One aspect that I don't really have a solution for, but I don't know how important it is, is removing the attribute itself from its input. I'm pretty sure rustc leaves out the attribute macro from the input, but to do that, we'd have to create a completely new syntax node. I guess we could do it when / after converting to a token tree.
Co-authored-by: Florian Diebold <[email protected]>
Diffstat (limited to 'crates/ra_hir_def/src')
-rw-r--r-- | crates/ra_hir_def/src/attr.rs | 4 | ||||
-rw-r--r-- | crates/ra_hir_def/src/body.rs | 6 | ||||
-rw-r--r-- | crates/ra_hir_def/src/docs.rs | 2 | ||||
-rw-r--r-- | crates/ra_hir_def/src/nameres/collector.rs | 73 | ||||
-rw-r--r-- | crates/ra_hir_def/src/nameres/raw.rs | 15 | ||||
-rw-r--r-- | crates/ra_hir_def/src/nameres/tests/macros.rs | 24 | ||||
-rw-r--r-- | crates/ra_hir_def/src/path.rs | 5 |
7 files changed, 118 insertions, 11 deletions
diff --git a/crates/ra_hir_def/src/attr.rs b/crates/ra_hir_def/src/attr.rs index 7f9a6e7ca..2f8f02d82 100644 --- a/crates/ra_hir_def/src/attr.rs +++ b/crates/ra_hir_def/src/attr.rs | |||
@@ -61,7 +61,9 @@ impl Attrs { | |||
61 | AdtId::UnionId(it) => attrs_from_ast(it.lookup_intern(db).ast_id, db), | 61 | AdtId::UnionId(it) => attrs_from_ast(it.lookup_intern(db).ast_id, db), |
62 | }, | 62 | }, |
63 | AttrDefId::TraitId(it) => attrs_from_ast(it.lookup_intern(db).ast_id, db), | 63 | AttrDefId::TraitId(it) => attrs_from_ast(it.lookup_intern(db).ast_id, db), |
64 | AttrDefId::MacroDefId(it) => attrs_from_ast(it.ast_id, db), | 64 | AttrDefId::MacroDefId(it) => { |
65 | it.ast_id.map_or_else(Default::default, |ast_id| attrs_from_ast(ast_id, db)) | ||
66 | } | ||
65 | AttrDefId::ImplId(it) => attrs_from_ast(it.lookup_intern(db).ast_id, db), | 67 | AttrDefId::ImplId(it) => attrs_from_ast(it.lookup_intern(db).ast_id, db), |
66 | AttrDefId::ConstId(it) => attrs_from_loc(it.lookup(db), db), | 68 | AttrDefId::ConstId(it) => attrs_from_loc(it.lookup(db), db), |
67 | AttrDefId::StaticId(it) => attrs_from_loc(it.lookup(db), db), | 69 | AttrDefId::StaticId(it) => attrs_from_loc(it.lookup(db), db), |
diff --git a/crates/ra_hir_def/src/body.rs b/crates/ra_hir_def/src/body.rs index ef1816836..7b385f3fd 100644 --- a/crates/ra_hir_def/src/body.rs +++ b/crates/ra_hir_def/src/body.rs | |||
@@ -6,7 +6,9 @@ pub mod scope; | |||
6 | use std::{ops::Index, sync::Arc}; | 6 | use std::{ops::Index, sync::Arc}; |
7 | 7 | ||
8 | use either::Either; | 8 | use either::Either; |
9 | use hir_expand::{hygiene::Hygiene, AstId, HirFileId, InFile, MacroDefId, MacroFileKind}; | 9 | use hir_expand::{ |
10 | hygiene::Hygiene, AstId, HirFileId, InFile, MacroCallKind, MacroDefId, MacroFileKind, | ||
11 | }; | ||
10 | use ra_arena::{map::ArenaMap, Arena}; | 12 | use ra_arena::{map::ArenaMap, Arena}; |
11 | use ra_syntax::{ast, AstNode, AstPtr}; | 13 | use ra_syntax::{ast, AstNode, AstPtr}; |
12 | use rustc_hash::FxHashMap; | 14 | use rustc_hash::FxHashMap; |
@@ -46,7 +48,7 @@ impl Expander { | |||
46 | 48 | ||
47 | if let Some(path) = macro_call.path().and_then(|path| self.parse_path(path)) { | 49 | if let Some(path) = macro_call.path().and_then(|path| self.parse_path(path)) { |
48 | if let Some(def) = self.resolve_path_as_macro(db, &path) { | 50 | if let Some(def) = self.resolve_path_as_macro(db, &path) { |
49 | let call_id = def.as_call_id(db, ast_id); | 51 | let call_id = def.as_call_id(db, MacroCallKind::FnLike(ast_id)); |
50 | let file_id = call_id.as_file(MacroFileKind::Expr); | 52 | let file_id = call_id.as_file(MacroFileKind::Expr); |
51 | if let Some(node) = db.parse_or_expand(file_id) { | 53 | if let Some(node) = db.parse_or_expand(file_id) { |
52 | if let Some(expr) = ast::Expr::cast(node) { | 54 | if let Some(expr) = ast::Expr::cast(node) { |
diff --git a/crates/ra_hir_def/src/docs.rs b/crates/ra_hir_def/src/docs.rs index 3fc6d6934..61727bd26 100644 --- a/crates/ra_hir_def/src/docs.rs +++ b/crates/ra_hir_def/src/docs.rs | |||
@@ -60,7 +60,7 @@ impl Documentation { | |||
60 | docs_from_ast(&src.value[it.local_id]) | 60 | docs_from_ast(&src.value[it.local_id]) |
61 | } | 61 | } |
62 | AttrDefId::TraitId(it) => docs_from_ast(&it.source(db).value), | 62 | AttrDefId::TraitId(it) => docs_from_ast(&it.source(db).value), |
63 | AttrDefId::MacroDefId(it) => docs_from_ast(&it.ast_id.to_node(db)), | 63 | AttrDefId::MacroDefId(it) => docs_from_ast(&it.ast_id?.to_node(db)), |
64 | AttrDefId::ConstId(it) => docs_from_ast(&it.lookup(db).source(db).value), | 64 | AttrDefId::ConstId(it) => docs_from_ast(&it.lookup(db).source(db).value), |
65 | AttrDefId::StaticId(it) => docs_from_ast(&it.lookup(db).source(db).value), | 65 | AttrDefId::StaticId(it) => docs_from_ast(&it.lookup(db).source(db).value), |
66 | AttrDefId::FunctionId(it) => docs_from_ast(&it.lookup(db).source(db).value), | 66 | AttrDefId::FunctionId(it) => docs_from_ast(&it.lookup(db).source(db).value), |
diff --git a/crates/ra_hir_def/src/nameres/collector.rs b/crates/ra_hir_def/src/nameres/collector.rs index 9d948d4f4..08693cb13 100644 --- a/crates/ra_hir_def/src/nameres/collector.rs +++ b/crates/ra_hir_def/src/nameres/collector.rs | |||
@@ -4,9 +4,10 @@ | |||
4 | //! resolves imports and expands macros. | 4 | //! resolves imports and expands macros. |
5 | 5 | ||
6 | use hir_expand::{ | 6 | use hir_expand::{ |
7 | builtin_derive::find_builtin_derive, | ||
7 | builtin_macro::find_builtin_macro, | 8 | builtin_macro::find_builtin_macro, |
8 | name::{self, AsName, Name}, | 9 | name::{self, AsName, Name}, |
9 | HirFileId, MacroCallId, MacroDefId, MacroDefKind, MacroFileKind, | 10 | HirFileId, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind, MacroFileKind, |
10 | }; | 11 | }; |
11 | use ra_cfg::CfgOptions; | 12 | use ra_cfg::CfgOptions; |
12 | use ra_db::{CrateId, FileId}; | 13 | use ra_db::{CrateId, FileId}; |
@@ -58,6 +59,7 @@ pub(super) fn collect_defs(db: &impl DefDatabase, mut def_map: CrateDefMap) -> C | |||
58 | glob_imports: FxHashMap::default(), | 59 | glob_imports: FxHashMap::default(), |
59 | unresolved_imports: Vec::new(), | 60 | unresolved_imports: Vec::new(), |
60 | unexpanded_macros: Vec::new(), | 61 | unexpanded_macros: Vec::new(), |
62 | unexpanded_attribute_macros: Vec::new(), | ||
61 | mod_dirs: FxHashMap::default(), | 63 | mod_dirs: FxHashMap::default(), |
62 | macro_stack_monitor: MacroStackMonitor::default(), | 64 | macro_stack_monitor: MacroStackMonitor::default(), |
63 | poison_macros: FxHashSet::default(), | 65 | poison_macros: FxHashSet::default(), |
@@ -102,6 +104,7 @@ struct DefCollector<'a, DB> { | |||
102 | glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, LocalImportId)>>, | 104 | glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, LocalImportId)>>, |
103 | unresolved_imports: Vec<(LocalModuleId, LocalImportId, raw::ImportData)>, | 105 | unresolved_imports: Vec<(LocalModuleId, LocalImportId, raw::ImportData)>, |
104 | unexpanded_macros: Vec<(LocalModuleId, AstId<ast::MacroCall>, Path)>, | 106 | unexpanded_macros: Vec<(LocalModuleId, AstId<ast::MacroCall>, Path)>, |
107 | unexpanded_attribute_macros: Vec<(LocalModuleId, AstId<ast::ModuleItem>, Path)>, | ||
105 | mod_dirs: FxHashMap<LocalModuleId, ModDir>, | 108 | mod_dirs: FxHashMap<LocalModuleId, ModDir>, |
106 | 109 | ||
107 | /// Some macro use `$tt:tt which mean we have to handle the macro perfectly | 110 | /// Some macro use `$tt:tt which mean we have to handle the macro perfectly |
@@ -470,6 +473,8 @@ where | |||
470 | 473 | ||
471 | fn resolve_macros(&mut self) -> ReachedFixedPoint { | 474 | fn resolve_macros(&mut self) -> ReachedFixedPoint { |
472 | let mut macros = std::mem::replace(&mut self.unexpanded_macros, Vec::new()); | 475 | let mut macros = std::mem::replace(&mut self.unexpanded_macros, Vec::new()); |
476 | let mut attribute_macros = | ||
477 | std::mem::replace(&mut self.unexpanded_attribute_macros, Vec::new()); | ||
473 | let mut resolved = Vec::new(); | 478 | let mut resolved = Vec::new(); |
474 | let mut res = ReachedFixedPoint::Yes; | 479 | let mut res = ReachedFixedPoint::Yes; |
475 | macros.retain(|(module_id, ast_id, path)| { | 480 | macros.retain(|(module_id, ast_id, path)| { |
@@ -482,7 +487,19 @@ where | |||
482 | ); | 487 | ); |
483 | 488 | ||
484 | if let Some(def) = resolved_res.resolved_def.take_macros() { | 489 | if let Some(def) = resolved_res.resolved_def.take_macros() { |
485 | let call_id = def.as_call_id(self.db, *ast_id); | 490 | let call_id = def.as_call_id(self.db, MacroCallKind::FnLike(*ast_id)); |
491 | resolved.push((*module_id, call_id, def)); | ||
492 | res = ReachedFixedPoint::No; | ||
493 | return false; | ||
494 | } | ||
495 | |||
496 | true | ||
497 | }); | ||
498 | attribute_macros.retain(|(module_id, ast_id, path)| { | ||
499 | let resolved_res = self.resolve_attribute_macro(path); | ||
500 | |||
501 | if let Some(def) = resolved_res { | ||
502 | let call_id = def.as_call_id(self.db, MacroCallKind::Attr(*ast_id)); | ||
486 | resolved.push((*module_id, call_id, def)); | 503 | resolved.push((*module_id, call_id, def)); |
487 | res = ReachedFixedPoint::No; | 504 | res = ReachedFixedPoint::No; |
488 | return false; | 505 | return false; |
@@ -492,6 +509,7 @@ where | |||
492 | }); | 509 | }); |
493 | 510 | ||
494 | self.unexpanded_macros = macros; | 511 | self.unexpanded_macros = macros; |
512 | self.unexpanded_attribute_macros = attribute_macros; | ||
495 | 513 | ||
496 | for (module_id, macro_call_id, macro_def_id) in resolved { | 514 | for (module_id, macro_call_id, macro_def_id) in resolved { |
497 | self.collect_macro_expansion(module_id, macro_call_id, macro_def_id); | 515 | self.collect_macro_expansion(module_id, macro_call_id, macro_def_id); |
@@ -500,6 +518,20 @@ where | |||
500 | res | 518 | res |
501 | } | 519 | } |
502 | 520 | ||
521 | fn resolve_attribute_macro(&self, path: &Path) -> Option<MacroDefId> { | ||
522 | // FIXME this is currently super hacky, just enough to support the | ||
523 | // built-in derives | ||
524 | if let Some(name) = path.as_ident() { | ||
525 | // FIXME this should actually be handled with the normal name | ||
526 | // resolution; the std lib defines built-in stubs for the derives, | ||
527 | // but these are new-style `macro`s, which we don't support yet | ||
528 | if let Some(def_id) = find_builtin_derive(name) { | ||
529 | return Some(def_id); | ||
530 | } | ||
531 | } | ||
532 | None | ||
533 | } | ||
534 | |||
503 | fn collect_macro_expansion( | 535 | fn collect_macro_expansion( |
504 | &mut self, | 536 | &mut self, |
505 | module_id: LocalModuleId, | 537 | module_id: LocalModuleId, |
@@ -587,7 +619,9 @@ where | |||
587 | .def_collector | 619 | .def_collector |
588 | .unresolved_imports | 620 | .unresolved_imports |
589 | .push((self.module_id, import_id, self.raw_items[import_id].clone())), | 621 | .push((self.module_id, import_id, self.raw_items[import_id].clone())), |
590 | raw::RawItemKind::Def(def) => self.define_def(&self.raw_items[def]), | 622 | raw::RawItemKind::Def(def) => { |
623 | self.define_def(&self.raw_items[def], &item.attrs) | ||
624 | } | ||
591 | raw::RawItemKind::Macro(mac) => self.collect_macro(&self.raw_items[mac]), | 625 | raw::RawItemKind::Macro(mac) => self.collect_macro(&self.raw_items[mac]), |
592 | raw::RawItemKind::Impl(imp) => { | 626 | raw::RawItemKind::Impl(imp) => { |
593 | let module = ModuleId { | 627 | let module = ModuleId { |
@@ -682,10 +716,16 @@ where | |||
682 | res | 716 | res |
683 | } | 717 | } |
684 | 718 | ||
685 | fn define_def(&mut self, def: &raw::DefData) { | 719 | fn define_def(&mut self, def: &raw::DefData, attrs: &Attrs) { |
686 | let module = ModuleId { krate: self.def_collector.def_map.krate, local_id: self.module_id }; | 720 | let module = ModuleId { krate: self.def_collector.def_map.krate, local_id: self.module_id }; |
687 | let ctx = LocationCtx::new(self.def_collector.db, module, self.file_id); | 721 | let ctx = LocationCtx::new(self.def_collector.db, module, self.file_id); |
688 | 722 | ||
723 | // FIXME: check attrs to see if this is an attribute macro invocation; | ||
724 | // in which case we don't add the invocation, just a single attribute | ||
725 | // macro invocation | ||
726 | |||
727 | self.collect_derives(attrs, def); | ||
728 | |||
689 | let name = def.name.clone(); | 729 | let name = def.name.clone(); |
690 | let def: PerNs = match def.kind { | 730 | let def: PerNs = match def.kind { |
691 | raw::DefKind::Function(ast_id) => { | 731 | raw::DefKind::Function(ast_id) => { |
@@ -736,6 +776,23 @@ where | |||
736 | self.def_collector.update(self.module_id, None, &[(name, resolution)]) | 776 | self.def_collector.update(self.module_id, None, &[(name, resolution)]) |
737 | } | 777 | } |
738 | 778 | ||
779 | fn collect_derives(&mut self, attrs: &Attrs, def: &raw::DefData) { | ||
780 | for derive_subtree in attrs.by_key("derive").tt_values() { | ||
781 | // for #[derive(Copy, Clone)], `derive_subtree` is the `(Copy, Clone)` subtree | ||
782 | for tt in &derive_subtree.token_trees { | ||
783 | let ident = match &tt { | ||
784 | tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => ident, | ||
785 | tt::TokenTree::Leaf(tt::Leaf::Punct(_)) => continue, // , is ok | ||
786 | _ => continue, // anything else would be an error (which we currently ignore) | ||
787 | }; | ||
788 | let path = Path::from_tt_ident(ident); | ||
789 | |||
790 | let ast_id = AstId::new(self.file_id, def.kind.ast_id()); | ||
791 | self.def_collector.unexpanded_attribute_macros.push((self.module_id, ast_id, path)); | ||
792 | } | ||
793 | } | ||
794 | } | ||
795 | |||
739 | fn collect_macro(&mut self, mac: &raw::MacroData) { | 796 | fn collect_macro(&mut self, mac: &raw::MacroData) { |
740 | let ast_id = AstId::new(self.file_id, mac.ast_id); | 797 | let ast_id = AstId::new(self.file_id, mac.ast_id); |
741 | 798 | ||
@@ -759,8 +816,8 @@ where | |||
759 | if is_macro_rules(&mac.path) { | 816 | if is_macro_rules(&mac.path) { |
760 | if let Some(name) = &mac.name { | 817 | if let Some(name) = &mac.name { |
761 | let macro_id = MacroDefId { | 818 | let macro_id = MacroDefId { |
762 | ast_id, | 819 | ast_id: Some(ast_id), |
763 | krate: self.def_collector.def_map.krate, | 820 | krate: Some(self.def_collector.def_map.krate), |
764 | kind: MacroDefKind::Declarative, | 821 | kind: MacroDefKind::Declarative, |
765 | }; | 822 | }; |
766 | self.def_collector.define_macro(self.module_id, name.clone(), macro_id, mac.export); | 823 | self.def_collector.define_macro(self.module_id, name.clone(), macro_id, mac.export); |
@@ -773,7 +830,8 @@ where | |||
773 | if let Some(macro_def) = mac.path.as_ident().and_then(|name| { | 830 | if let Some(macro_def) = mac.path.as_ident().and_then(|name| { |
774 | self.def_collector.def_map[self.module_id].scope.get_legacy_macro(&name) | 831 | self.def_collector.def_map[self.module_id].scope.get_legacy_macro(&name) |
775 | }) { | 832 | }) { |
776 | let macro_call_id = macro_def.as_call_id(self.def_collector.db, ast_id); | 833 | let macro_call_id = |
834 | macro_def.as_call_id(self.def_collector.db, MacroCallKind::FnLike(ast_id)); | ||
777 | 835 | ||
778 | self.def_collector.collect_macro_expansion(self.module_id, macro_call_id, macro_def); | 836 | self.def_collector.collect_macro_expansion(self.module_id, macro_call_id, macro_def); |
779 | return; | 837 | return; |
@@ -829,6 +887,7 @@ mod tests { | |||
829 | glob_imports: FxHashMap::default(), | 887 | glob_imports: FxHashMap::default(), |
830 | unresolved_imports: Vec::new(), | 888 | unresolved_imports: Vec::new(), |
831 | unexpanded_macros: Vec::new(), | 889 | unexpanded_macros: Vec::new(), |
890 | unexpanded_attribute_macros: Vec::new(), | ||
832 | mod_dirs: FxHashMap::default(), | 891 | mod_dirs: FxHashMap::default(), |
833 | macro_stack_monitor: monitor, | 892 | macro_stack_monitor: monitor, |
834 | poison_macros: FxHashSet::default(), | 893 | poison_macros: FxHashSet::default(), |
diff --git a/crates/ra_hir_def/src/nameres/raw.rs b/crates/ra_hir_def/src/nameres/raw.rs index de4e706c2..a2821e1c3 100644 --- a/crates/ra_hir_def/src/nameres/raw.rs +++ b/crates/ra_hir_def/src/nameres/raw.rs | |||
@@ -184,6 +184,21 @@ pub(super) enum DefKind { | |||
184 | TypeAlias(FileAstId<ast::TypeAliasDef>), | 184 | TypeAlias(FileAstId<ast::TypeAliasDef>), |
185 | } | 185 | } |
186 | 186 | ||
187 | impl DefKind { | ||
188 | pub fn ast_id(&self) -> FileAstId<ast::ModuleItem> { | ||
189 | match self { | ||
190 | DefKind::Function(it) => it.upcast(), | ||
191 | DefKind::Struct(it) => it.upcast(), | ||
192 | DefKind::Union(it) => it.upcast(), | ||
193 | DefKind::Enum(it) => it.upcast(), | ||
194 | DefKind::Const(it) => it.upcast(), | ||
195 | DefKind::Static(it) => it.upcast(), | ||
196 | DefKind::Trait(it) => it.upcast(), | ||
197 | DefKind::TypeAlias(it) => it.upcast(), | ||
198 | } | ||
199 | } | ||
200 | } | ||
201 | |||
187 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 202 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
188 | pub(super) struct Macro(RawId); | 203 | pub(super) struct Macro(RawId); |
189 | impl_arena_id!(Macro); | 204 | impl_arena_id!(Macro); |
diff --git a/crates/ra_hir_def/src/nameres/tests/macros.rs b/crates/ra_hir_def/src/nameres/tests/macros.rs index 704065633..cfa4ecb1a 100644 --- a/crates/ra_hir_def/src/nameres/tests/macros.rs +++ b/crates/ra_hir_def/src/nameres/tests/macros.rs | |||
@@ -600,3 +600,27 @@ fn macro_dollar_crate_is_correct_in_indirect_deps() { | |||
600 | ⋮bar: t v | 600 | ⋮bar: t v |
601 | "###); | 601 | "###); |
602 | } | 602 | } |
603 | |||
604 | #[test] | ||
605 | fn expand_derive() { | ||
606 | let map = compute_crate_def_map( | ||
607 | " | ||
608 | //- /main.rs | ||
609 | #[derive(Clone)] | ||
610 | struct Foo; | ||
611 | ", | ||
612 | ); | ||
613 | assert_eq!(map.modules[map.root].impls.len(), 1); | ||
614 | } | ||
615 | |||
616 | #[test] | ||
617 | fn expand_multiple_derive() { | ||
618 | let map = compute_crate_def_map( | ||
619 | " | ||
620 | //- /main.rs | ||
621 | #[derive(Copy, Clone)] | ||
622 | struct Foo; | ||
623 | ", | ||
624 | ); | ||
625 | assert_eq!(map.modules[map.root].impls.len(), 2); | ||
626 | } | ||
diff --git a/crates/ra_hir_def/src/path.rs b/crates/ra_hir_def/src/path.rs index 3030dcdf6..e547b2f03 100644 --- a/crates/ra_hir_def/src/path.rs +++ b/crates/ra_hir_def/src/path.rs | |||
@@ -199,6 +199,11 @@ impl Path { | |||
199 | name_ref.as_name().into() | 199 | name_ref.as_name().into() |
200 | } | 200 | } |
201 | 201 | ||
202 | /// Converts an `tt::Ident` into a single-identifier `Path`. | ||
203 | pub(crate) fn from_tt_ident(ident: &tt::Ident) -> Path { | ||
204 | ident.as_name().into() | ||
205 | } | ||
206 | |||
202 | /// `true` is this path is a single identifier, like `foo` | 207 | /// `true` is this path is a single identifier, like `foo` |
203 | pub fn is_ident(&self) -> bool { | 208 | pub fn is_ident(&self) -> bool { |
204 | self.kind == PathKind::Plain && self.segments.len() == 1 | 209 | self.kind == PathKind::Plain && self.segments.len() == 1 |