diff options
-rw-r--r-- | crates/ra_hir_def/src/item_scope.rs | 33 | ||||
-rw-r--r-- | crates/ra_hir_def/src/nameres/path_resolution.rs | 63 | ||||
-rw-r--r-- | crates/ra_hir_def/src/resolver.rs | 13 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/complete_path.rs | 13 | ||||
-rw-r--r-- | crates/ra_ide/src/marks.rs | 1 | ||||
-rw-r--r-- | crates/ra_mbe/src/subtree_source.rs | 5 | ||||
-rw-r--r-- | crates/ra_mbe/src/tests.rs | 22 |
7 files changed, 78 insertions, 72 deletions
diff --git a/crates/ra_hir_def/src/item_scope.rs b/crates/ra_hir_def/src/item_scope.rs index 6e958ca75..5e943b780 100644 --- a/crates/ra_hir_def/src/item_scope.rs +++ b/crates/ra_hir_def/src/item_scope.rs | |||
@@ -30,7 +30,7 @@ pub struct ItemScope { | |||
30 | legacy_macros: FxHashMap<Name, MacroDefId>, | 30 | legacy_macros: FxHashMap<Name, MacroDefId>, |
31 | } | 31 | } |
32 | 32 | ||
33 | static BUILTIN_SCOPE: Lazy<FxHashMap<Name, PerNs>> = Lazy::new(|| { | 33 | pub(crate) static BUILTIN_SCOPE: Lazy<FxHashMap<Name, PerNs>> = Lazy::new(|| { |
34 | BuiltinType::ALL | 34 | BuiltinType::ALL |
35 | .iter() | 35 | .iter() |
36 | .map(|(name, ty)| (name.clone(), PerNs::types(ty.clone().into(), Visibility::Public))) | 36 | .map(|(name, ty)| (name.clone(), PerNs::types(ty.clone().into(), Visibility::Public))) |
@@ -40,9 +40,9 @@ static BUILTIN_SCOPE: Lazy<FxHashMap<Name, PerNs>> = Lazy::new(|| { | |||
40 | /// Shadow mode for builtin type which can be shadowed by module. | 40 | /// Shadow mode for builtin type which can be shadowed by module. |
41 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] | 41 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] |
42 | pub(crate) enum BuiltinShadowMode { | 42 | pub(crate) enum BuiltinShadowMode { |
43 | // Prefer Module | 43 | /// Prefer user-defined modules (or other types) over builtins. |
44 | Module, | 44 | Module, |
45 | // Prefer Other Types | 45 | /// Prefer builtins over user-defined modules (but not other types). |
46 | Other, | 46 | Other, |
47 | } | 47 | } |
48 | 48 | ||
@@ -51,7 +51,7 @@ pub(crate) enum BuiltinShadowMode { | |||
51 | impl ItemScope { | 51 | impl ItemScope { |
52 | pub fn entries<'a>(&'a self) -> impl Iterator<Item = (&'a Name, PerNs)> + 'a { | 52 | pub fn entries<'a>(&'a self) -> impl Iterator<Item = (&'a Name, PerNs)> + 'a { |
53 | //FIXME: shadowing | 53 | //FIXME: shadowing |
54 | self.visible.iter().chain(BUILTIN_SCOPE.iter()).map(|(n, def)| (n, *def)) | 54 | self.visible.iter().map(|(n, def)| (n, *def)) |
55 | } | 55 | } |
56 | 56 | ||
57 | pub fn entries_without_primitives<'a>( | 57 | pub fn entries_without_primitives<'a>( |
@@ -79,29 +79,8 @@ impl ItemScope { | |||
79 | } | 79 | } |
80 | 80 | ||
81 | /// Get a name from current module scope, legacy macros are not included | 81 | /// Get a name from current module scope, legacy macros are not included |
82 | pub(crate) fn get(&self, name: &Name, shadow: BuiltinShadowMode) -> PerNs { | 82 | pub(crate) fn get(&self, name: &Name) -> PerNs { |
83 | match shadow { | 83 | self.visible.get(name).copied().unwrap_or_else(PerNs::none) |
84 | BuiltinShadowMode::Module => self | ||
85 | .visible | ||
86 | .get(name) | ||
87 | .or_else(|| BUILTIN_SCOPE.get(name)) | ||
88 | .copied() | ||
89 | .unwrap_or_else(PerNs::none), | ||
90 | BuiltinShadowMode::Other => { | ||
91 | let item = self.visible.get(name).copied(); | ||
92 | if let Some(def) = item { | ||
93 | if let Some(ModuleDefId::ModuleId(_)) = def.take_types() { | ||
94 | return BUILTIN_SCOPE | ||
95 | .get(name) | ||
96 | .copied() | ||
97 | .or(item) | ||
98 | .unwrap_or_else(PerNs::none); | ||
99 | } | ||
100 | } | ||
101 | |||
102 | item.or_else(|| BUILTIN_SCOPE.get(name).copied()).unwrap_or_else(PerNs::none) | ||
103 | } | ||
104 | } | ||
105 | } | 84 | } |
106 | 85 | ||
107 | pub(crate) fn name_of(&self, item: ItemInNs) -> Option<(&Name, Visibility)> { | 86 | pub(crate) fn name_of(&self, item: ItemInNs) -> Option<(&Name, Visibility)> { |
diff --git a/crates/ra_hir_def/src/nameres/path_resolution.rs b/crates/ra_hir_def/src/nameres/path_resolution.rs index fd6422d60..c058e70aa 100644 --- a/crates/ra_hir_def/src/nameres/path_resolution.rs +++ b/crates/ra_hir_def/src/nameres/path_resolution.rs | |||
@@ -18,6 +18,7 @@ use test_utils::tested_by; | |||
18 | 18 | ||
19 | use crate::{ | 19 | use crate::{ |
20 | db::DefDatabase, | 20 | db::DefDatabase, |
21 | item_scope::BUILTIN_SCOPE, | ||
21 | nameres::{BuiltinShadowMode, CrateDefMap}, | 22 | nameres::{BuiltinShadowMode, CrateDefMap}, |
22 | path::{ModPath, PathKind}, | 23 | path::{ModPath, PathKind}, |
23 | per_ns::PerNs, | 24 | per_ns::PerNs, |
@@ -103,15 +104,6 @@ impl CrateDefMap { | |||
103 | path: &ModPath, | 104 | path: &ModPath, |
104 | shadow: BuiltinShadowMode, | 105 | shadow: BuiltinShadowMode, |
105 | ) -> ResolvePathResult { | 106 | ) -> ResolvePathResult { |
106 | // if it is not the last segment, we prefer the module to the builtin | ||
107 | let prefer_module = |index| { | ||
108 | if index == path.segments.len() - 1 { | ||
109 | shadow | ||
110 | } else { | ||
111 | BuiltinShadowMode::Module | ||
112 | } | ||
113 | }; | ||
114 | |||
115 | let mut segments = path.segments.iter().enumerate(); | 107 | let mut segments = path.segments.iter().enumerate(); |
116 | let mut curr_per_ns: PerNs = match path.kind { | 108 | let mut curr_per_ns: PerNs = match path.kind { |
117 | PathKind::DollarCrate(krate) => { | 109 | PathKind::DollarCrate(krate) => { |
@@ -140,20 +132,29 @@ impl CrateDefMap { | |||
140 | if self.edition == Edition::Edition2015 | 132 | if self.edition == Edition::Edition2015 |
141 | && (path.kind == PathKind::Abs || mode == ResolveMode::Import) => | 133 | && (path.kind == PathKind::Abs || mode == ResolveMode::Import) => |
142 | { | 134 | { |
143 | let (idx, segment) = match segments.next() { | 135 | let (_, segment) = match segments.next() { |
144 | Some((idx, segment)) => (idx, segment), | 136 | Some((idx, segment)) => (idx, segment), |
145 | None => return ResolvePathResult::empty(ReachedFixedPoint::Yes), | 137 | None => return ResolvePathResult::empty(ReachedFixedPoint::Yes), |
146 | }; | 138 | }; |
147 | log::debug!("resolving {:?} in crate root (+ extern prelude)", segment); | 139 | log::debug!("resolving {:?} in crate root (+ extern prelude)", segment); |
148 | self.resolve_name_in_crate_root_or_extern_prelude(&segment, prefer_module(idx)) | 140 | self.resolve_name_in_crate_root_or_extern_prelude(&segment) |
149 | } | 141 | } |
150 | PathKind::Plain => { | 142 | PathKind::Plain => { |
151 | let (idx, segment) = match segments.next() { | 143 | let (_, segment) = match segments.next() { |
152 | Some((idx, segment)) => (idx, segment), | 144 | Some((idx, segment)) => (idx, segment), |
153 | None => return ResolvePathResult::empty(ReachedFixedPoint::Yes), | 145 | None => return ResolvePathResult::empty(ReachedFixedPoint::Yes), |
154 | }; | 146 | }; |
147 | // The first segment may be a builtin type. If the path has more | ||
148 | // than one segment, we first try resolving it as a module | ||
149 | // anyway. | ||
150 | // FIXME: If the next segment doesn't resolve in the module and | ||
151 | // BuiltinShadowMode wasn't Module, then we need to try | ||
152 | // resolving it as a builtin. | ||
153 | let prefer_module = | ||
154 | if path.segments.len() == 1 { shadow } else { BuiltinShadowMode::Module }; | ||
155 | |||
155 | log::debug!("resolving {:?} in module", segment); | 156 | log::debug!("resolving {:?} in module", segment); |
156 | self.resolve_name_in_module(db, original_module, &segment, prefer_module(idx)) | 157 | self.resolve_name_in_module(db, original_module, &segment, prefer_module) |
157 | } | 158 | } |
158 | PathKind::Super(lvl) => { | 159 | PathKind::Super(lvl) => { |
159 | let m = successors(Some(original_module), |m| self.modules[*m].parent) | 160 | let m = successors(Some(original_module), |m| self.modules[*m].parent) |
@@ -216,7 +217,7 @@ impl CrateDefMap { | |||
216 | } | 217 | } |
217 | 218 | ||
218 | // Since it is a qualified path here, it should not contains legacy macros | 219 | // Since it is a qualified path here, it should not contains legacy macros |
219 | self[module.local_id].scope.get(&segment, prefer_module(i)) | 220 | self[module.local_id].scope.get(&segment) |
220 | } | 221 | } |
221 | ModuleDefId::AdtId(AdtId::EnumId(e)) => { | 222 | ModuleDefId::AdtId(AdtId::EnumId(e)) => { |
222 | // enum variant | 223 | // enum variant |
@@ -275,33 +276,35 @@ impl CrateDefMap { | |||
275 | .scope | 276 | .scope |
276 | .get_legacy_macro(name) | 277 | .get_legacy_macro(name) |
277 | .map_or_else(PerNs::none, |m| PerNs::macros(m, Visibility::Public)); | 278 | .map_or_else(PerNs::none, |m| PerNs::macros(m, Visibility::Public)); |
278 | let from_scope = self[module].scope.get(name, shadow); | 279 | let from_scope = self[module].scope.get(name); |
280 | let from_builtin = BUILTIN_SCOPE.get(name).copied().unwrap_or_else(PerNs::none); | ||
281 | let from_scope_or_builtin = match shadow { | ||
282 | BuiltinShadowMode::Module => from_scope.or(from_builtin), | ||
283 | BuiltinShadowMode::Other => { | ||
284 | if let Some(ModuleDefId::ModuleId(_)) = from_scope.take_types() { | ||
285 | from_builtin.or(from_scope) | ||
286 | } else { | ||
287 | from_scope.or(from_builtin) | ||
288 | } | ||
289 | } | ||
290 | }; | ||
279 | let from_extern_prelude = self | 291 | let from_extern_prelude = self |
280 | .extern_prelude | 292 | .extern_prelude |
281 | .get(name) | 293 | .get(name) |
282 | .map_or(PerNs::none(), |&it| PerNs::types(it, Visibility::Public)); | 294 | .map_or(PerNs::none(), |&it| PerNs::types(it, Visibility::Public)); |
283 | let from_prelude = self.resolve_in_prelude(db, name, shadow); | 295 | let from_prelude = self.resolve_in_prelude(db, name); |
284 | 296 | ||
285 | from_legacy_macro.or(from_scope).or(from_extern_prelude).or(from_prelude) | 297 | from_legacy_macro.or(from_scope_or_builtin).or(from_extern_prelude).or(from_prelude) |
286 | } | 298 | } |
287 | 299 | ||
288 | fn resolve_name_in_crate_root_or_extern_prelude( | 300 | fn resolve_name_in_crate_root_or_extern_prelude(&self, name: &Name) -> PerNs { |
289 | &self, | 301 | let from_crate_root = self[self.root].scope.get(name); |
290 | name: &Name, | ||
291 | shadow: BuiltinShadowMode, | ||
292 | ) -> PerNs { | ||
293 | let from_crate_root = self[self.root].scope.get(name, shadow); | ||
294 | let from_extern_prelude = self.resolve_name_in_extern_prelude(name); | 302 | let from_extern_prelude = self.resolve_name_in_extern_prelude(name); |
295 | 303 | ||
296 | from_crate_root.or(from_extern_prelude) | 304 | from_crate_root.or(from_extern_prelude) |
297 | } | 305 | } |
298 | 306 | ||
299 | fn resolve_in_prelude( | 307 | fn resolve_in_prelude(&self, db: &impl DefDatabase, name: &Name) -> PerNs { |
300 | &self, | ||
301 | db: &impl DefDatabase, | ||
302 | name: &Name, | ||
303 | shadow: BuiltinShadowMode, | ||
304 | ) -> PerNs { | ||
305 | if let Some(prelude) = self.prelude { | 308 | if let Some(prelude) = self.prelude { |
306 | let keep; | 309 | let keep; |
307 | let def_map = if prelude.krate == self.krate { | 310 | let def_map = if prelude.krate == self.krate { |
@@ -311,7 +314,7 @@ impl CrateDefMap { | |||
311 | keep = db.crate_def_map(prelude.krate); | 314 | keep = db.crate_def_map(prelude.krate); |
312 | &keep | 315 | &keep |
313 | }; | 316 | }; |
314 | def_map[prelude.local_id].scope.get(name, shadow) | 317 | def_map[prelude.local_id].scope.get(name) |
315 | } else { | 318 | } else { |
316 | PerNs::none() | 319 | PerNs::none() |
317 | } | 320 | } |
diff --git a/crates/ra_hir_def/src/resolver.rs b/crates/ra_hir_def/src/resolver.rs index 5365b80e2..9dd4fa555 100644 --- a/crates/ra_hir_def/src/resolver.rs +++ b/crates/ra_hir_def/src/resolver.rs | |||
@@ -15,7 +15,7 @@ use crate::{ | |||
15 | db::DefDatabase, | 15 | db::DefDatabase, |
16 | expr::{ExprId, PatId}, | 16 | expr::{ExprId, PatId}, |
17 | generics::GenericParams, | 17 | generics::GenericParams, |
18 | item_scope::BuiltinShadowMode, | 18 | item_scope::{BuiltinShadowMode, BUILTIN_SCOPE}, |
19 | nameres::CrateDefMap, | 19 | nameres::CrateDefMap, |
20 | path::{ModPath, PathKind}, | 20 | path::{ModPath, PathKind}, |
21 | per_ns::PerNs, | 21 | per_ns::PerNs, |
@@ -193,7 +193,7 @@ impl Resolver { | |||
193 | return Some((res, idx)); | 193 | return Some((res, idx)); |
194 | } | 194 | } |
195 | Scope::LocalItemsScope(body) => { | 195 | Scope::LocalItemsScope(body) => { |
196 | let def = body.item_scope.get(first_name, BuiltinShadowMode::Other); | 196 | let def = body.item_scope.get(first_name); |
197 | if let Some(res) = to_type_ns(def) { | 197 | if let Some(res) = to_type_ns(def) { |
198 | return Some((res, None)); | 198 | return Some((res, None)); |
199 | } | 199 | } |
@@ -335,8 +335,10 @@ impl Resolver { | |||
335 | }; | 335 | }; |
336 | } | 336 | } |
337 | Scope::LocalItemsScope(body) => { | 337 | Scope::LocalItemsScope(body) => { |
338 | let def = body.item_scope.get(first_name, BuiltinShadowMode::Other); | 338 | // we don't bother looking in the builtin scope here because there are no builtin values |
339 | if let Some(res) = to_value_ns(def) { | 339 | let def = to_value_ns(body.item_scope.get(first_name)); |
340 | |||
341 | if let Some(res) = def { | ||
340 | return Some(ResolveValueResult::ValueNs(res)); | 342 | return Some(ResolveValueResult::ValueNs(res)); |
341 | } | 343 | } |
342 | } | 344 | } |
@@ -476,6 +478,9 @@ impl Scope { | |||
476 | m.crate_def_map.extern_prelude.iter().for_each(|(name, &def)| { | 478 | m.crate_def_map.extern_prelude.iter().for_each(|(name, &def)| { |
477 | f(name.clone(), ScopeDef::PerNs(PerNs::types(def, Visibility::Public))); | 479 | f(name.clone(), ScopeDef::PerNs(PerNs::types(def, Visibility::Public))); |
478 | }); | 480 | }); |
481 | BUILTIN_SCOPE.iter().for_each(|(name, &def)| { | ||
482 | f(name.clone(), ScopeDef::PerNs(def)); | ||
483 | }); | ||
479 | if let Some(prelude) = m.crate_def_map.prelude { | 484 | if let Some(prelude) = m.crate_def_map.prelude { |
480 | let prelude_def_map = db.crate_def_map(prelude.krate); | 485 | let prelude_def_map = db.crate_def_map(prelude.krate); |
481 | prelude_def_map[prelude.local_id].scope.entries().for_each(|(name, def)| { | 486 | prelude_def_map[prelude.local_id].scope.entries().for_each(|(name, def)| { |
diff --git a/crates/ra_ide/src/completion/complete_path.rs b/crates/ra_ide/src/completion/complete_path.rs index af24e9f48..2d7f09a6c 100644 --- a/crates/ra_ide/src/completion/complete_path.rs +++ b/crates/ra_ide/src/completion/complete_path.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! Completion of paths, including when writing a single name. |
2 | 2 | ||
3 | use hir::{Adt, PathResolution, ScopeDef}; | 3 | use hir::{Adt, PathResolution, ScopeDef}; |
4 | use ra_syntax::AstNode; | 4 | use ra_syntax::AstNode; |
@@ -20,10 +20,6 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) { | |||
20 | let module_scope = module.scope(ctx.db); | 20 | let module_scope = module.scope(ctx.db); |
21 | for (name, def) in module_scope { | 21 | for (name, def) in module_scope { |
22 | if ctx.use_item_syntax.is_some() { | 22 | if ctx.use_item_syntax.is_some() { |
23 | if let hir::ScopeDef::ModuleDef(hir::ModuleDef::BuiltinType(..)) = def { | ||
24 | tested_by!(dont_complete_primitive_in_use); | ||
25 | continue; | ||
26 | } | ||
27 | if let ScopeDef::Unknown = def { | 23 | if let ScopeDef::Unknown = def { |
28 | if let Some(name_ref) = ctx.name_ref_syntax.as_ref() { | 24 | if let Some(name_ref) = ctx.name_ref_syntax.as_ref() { |
29 | if name_ref.syntax().text() == name.to_string().as_str() { | 25 | if name_ref.syntax().text() == name.to_string().as_str() { |
@@ -125,12 +121,17 @@ mod tests { | |||
125 | 121 | ||
126 | #[test] | 122 | #[test] |
127 | fn dont_complete_primitive_in_use() { | 123 | fn dont_complete_primitive_in_use() { |
128 | covers!(dont_complete_primitive_in_use); | ||
129 | let completions = do_completion(r"use self::<|>;", CompletionKind::BuiltinType); | 124 | let completions = do_completion(r"use self::<|>;", CompletionKind::BuiltinType); |
130 | assert!(completions.is_empty()); | 125 | assert!(completions.is_empty()); |
131 | } | 126 | } |
132 | 127 | ||
133 | #[test] | 128 | #[test] |
129 | fn dont_complete_primitive_in_module_scope() { | ||
130 | let completions = do_completion(r"fn foo() { self::<|> }", CompletionKind::BuiltinType); | ||
131 | assert!(completions.is_empty()); | ||
132 | } | ||
133 | |||
134 | #[test] | ||
134 | fn completes_primitives() { | 135 | fn completes_primitives() { |
135 | let completions = | 136 | let completions = |
136 | do_completion(r"fn main() { let _: <|> = 92; }", CompletionKind::BuiltinType); | 137 | do_completion(r"fn main() { let _: <|> = 92; }", CompletionKind::BuiltinType); |
diff --git a/crates/ra_ide/src/marks.rs b/crates/ra_ide/src/marks.rs index 5bf4d2062..bcb67e373 100644 --- a/crates/ra_ide/src/marks.rs +++ b/crates/ra_ide/src/marks.rs | |||
@@ -10,6 +10,5 @@ test_utils::marks!( | |||
10 | goto_def_for_field_init_shorthand | 10 | goto_def_for_field_init_shorthand |
11 | call_info_bad_offset | 11 | call_info_bad_offset |
12 | dont_complete_current_use | 12 | dont_complete_current_use |
13 | dont_complete_primitive_in_use | ||
14 | test_resolve_parent_module_on_module_decl | 13 | test_resolve_parent_module_on_module_decl |
15 | ); | 14 | ); |
diff --git a/crates/ra_mbe/src/subtree_source.rs b/crates/ra_mbe/src/subtree_source.rs index dacca8279..91e324db9 100644 --- a/crates/ra_mbe/src/subtree_source.rs +++ b/crates/ra_mbe/src/subtree_source.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use ra_parser::{Token, TokenSource}; | 3 | use ra_parser::{Token, TokenSource}; |
4 | use ra_syntax::{lex_single_valid_syntax_kind, SmolStr, SyntaxKind, SyntaxKind::*, T}; | 4 | use ra_syntax::{lex_single_syntax_kind, SmolStr, SyntaxKind, SyntaxKind::*, T}; |
5 | use std::cell::{Cell, Ref, RefCell}; | 5 | use std::cell::{Cell, Ref, RefCell}; |
6 | use tt::buffer::{Cursor, TokenBuffer}; | 6 | use tt::buffer::{Cursor, TokenBuffer}; |
7 | 7 | ||
@@ -129,7 +129,8 @@ fn convert_delim(d: Option<tt::DelimiterKind>, closing: bool) -> TtToken { | |||
129 | } | 129 | } |
130 | 130 | ||
131 | fn convert_literal(l: &tt::Literal) -> TtToken { | 131 | fn convert_literal(l: &tt::Literal) -> TtToken { |
132 | let kind = lex_single_valid_syntax_kind(&l.text) | 132 | let kind = lex_single_syntax_kind(&l.text) |
133 | .map(|(kind, _error)| kind) | ||
133 | .filter(|kind| kind.is_literal()) | 134 | .filter(|kind| kind.is_literal()) |
134 | .unwrap_or_else(|| match l.text.as_ref() { | 135 | .unwrap_or_else(|| match l.text.as_ref() { |
135 | "true" => T![true], | 136 | "true" => T![true], |
diff --git a/crates/ra_mbe/src/tests.rs b/crates/ra_mbe/src/tests.rs index e0d689704..cb228702f 100644 --- a/crates/ra_mbe/src/tests.rs +++ b/crates/ra_mbe/src/tests.rs | |||
@@ -1374,14 +1374,22 @@ pub(crate) struct MacroFixture { | |||
1374 | 1374 | ||
1375 | impl MacroFixture { | 1375 | impl MacroFixture { |
1376 | pub(crate) fn expand_tt(&self, invocation: &str) -> tt::Subtree { | 1376 | pub(crate) fn expand_tt(&self, invocation: &str) -> tt::Subtree { |
1377 | let source_file = ast::SourceFile::parse(invocation).ok().unwrap(); | 1377 | self.try_expand_tt(invocation).unwrap() |
1378 | } | ||
1379 | |||
1380 | fn try_expand_tt(&self, invocation: &str) -> Result<tt::Subtree, ExpandError> { | ||
1381 | let source_file = ast::SourceFile::parse(invocation).tree(); | ||
1378 | let macro_invocation = | 1382 | let macro_invocation = |
1379 | source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap(); | 1383 | source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap(); |
1380 | 1384 | ||
1381 | let (invocation_tt, _) = | 1385 | let (invocation_tt, _) = |
1382 | ast_to_token_tree(¯o_invocation.token_tree().unwrap()).unwrap(); | 1386 | ast_to_token_tree(¯o_invocation.token_tree().unwrap()).unwrap(); |
1383 | 1387 | ||
1384 | self.rules.expand(&invocation_tt).unwrap() | 1388 | self.rules.expand(&invocation_tt) |
1389 | } | ||
1390 | |||
1391 | fn assert_expand_err(&self, invocation: &str, err: &ExpandError) { | ||
1392 | assert_eq!(self.try_expand_tt(invocation).as_ref(), Err(err)); | ||
1385 | } | 1393 | } |
1386 | 1394 | ||
1387 | fn expand_items(&self, invocation: &str) -> SyntaxNode { | 1395 | fn expand_items(&self, invocation: &str) -> SyntaxNode { |
@@ -1539,3 +1547,13 @@ fn test_repeat_bad_var() { | |||
1539 | ) | 1547 | ) |
1540 | .assert_expand_items("foo!(b0 b1);", "b0 b1"); | 1548 | .assert_expand_items("foo!(b0 b1);", "b0 b1"); |
1541 | } | 1549 | } |
1550 | |||
1551 | #[test] | ||
1552 | fn test_expand_bad_literal() { | ||
1553 | parse_macro( | ||
1554 | r#" | ||
1555 | macro_rules! foo { ($i:literal) => {}; } | ||
1556 | "#, | ||
1557 | ) | ||
1558 | .assert_expand_err(r#"foo!(&k");"#, &ExpandError::NoMatchingRule); | ||
1559 | } | ||