diff options
Diffstat (limited to 'crates')
56 files changed, 2995 insertions, 2359 deletions
diff --git a/crates/hir/src/attrs.rs b/crates/hir/src/attrs.rs index b9c695921..dab8da7bb 100644 --- a/crates/hir/src/attrs.rs +++ b/crates/hir/src/attrs.rs | |||
@@ -11,8 +11,8 @@ use hir_ty::db::HirDatabase; | |||
11 | use syntax::ast; | 11 | use syntax::ast; |
12 | 12 | ||
13 | use crate::{ | 13 | use crate::{ |
14 | Adt, Const, ConstParam, Enum, Field, Function, GenericParam, LifetimeParam, MacroDef, Module, | 14 | Adt, Const, ConstParam, Enum, Field, Function, GenericParam, Impl, LifetimeParam, MacroDef, |
15 | ModuleDef, Static, Struct, Trait, TypeAlias, TypeParam, Union, Variant, | 15 | Module, ModuleDef, Static, Struct, Trait, TypeAlias, TypeParam, Union, Variant, |
16 | }; | 16 | }; |
17 | 17 | ||
18 | pub trait HasAttrs { | 18 | pub trait HasAttrs { |
@@ -64,6 +64,7 @@ impl_has_attrs![ | |||
64 | (Adt, AdtId), | 64 | (Adt, AdtId), |
65 | (Module, ModuleId), | 65 | (Module, ModuleId), |
66 | (GenericParam, GenericParamId), | 66 | (GenericParam, GenericParamId), |
67 | (Impl, ImplId), | ||
67 | ]; | 68 | ]; |
68 | 69 | ||
69 | macro_rules! impl_has_attrs_enum { | 70 | macro_rules! impl_has_attrs_enum { |
diff --git a/crates/hir/src/db.rs b/crates/hir/src/db.rs index d444f4bbb..1902a8d16 100644 --- a/crates/hir/src/db.rs +++ b/crates/hir/src/db.rs | |||
@@ -3,10 +3,10 @@ | |||
3 | pub use hir_def::db::{ | 3 | pub use hir_def::db::{ |
4 | AttrsQuery, BlockDefMapQuery, BodyQuery, BodyWithSourceMapQuery, ConstDataQuery, | 4 | AttrsQuery, BlockDefMapQuery, BodyQuery, BodyWithSourceMapQuery, ConstDataQuery, |
5 | CrateDefMapQueryQuery, CrateLangItemsQuery, DefDatabase, DefDatabaseStorage, EnumDataQuery, | 5 | CrateDefMapQueryQuery, CrateLangItemsQuery, DefDatabase, DefDatabaseStorage, EnumDataQuery, |
6 | ExprScopesQuery, FunctionDataQuery, GenericParamsQuery, ImplDataQuery, ImportMapQuery, | 6 | ExprScopesQuery, FileItemTreeQuery, FunctionDataQuery, GenericParamsQuery, ImplDataQuery, |
7 | InternConstQuery, InternDatabase, InternDatabaseStorage, InternEnumQuery, InternFunctionQuery, | 7 | ImportMapQuery, InternConstQuery, InternDatabase, InternDatabaseStorage, InternEnumQuery, |
8 | InternImplQuery, InternStaticQuery, InternStructQuery, InternTraitQuery, InternTypeAliasQuery, | 8 | InternFunctionQuery, InternImplQuery, InternStaticQuery, InternStructQuery, InternTraitQuery, |
9 | InternUnionQuery, ItemTreeQuery, LangItemQuery, StaticDataQuery, StructDataQuery, | 9 | InternTypeAliasQuery, InternUnionQuery, LangItemQuery, StaticDataQuery, StructDataQuery, |
10 | TraitDataQuery, TypeAliasDataQuery, UnionDataQuery, | 10 | TraitDataQuery, TypeAliasDataQuery, UnionDataQuery, |
11 | }; | 11 | }; |
12 | pub use hir_expand::db::{ | 12 | pub use hir_expand::db::{ |
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 861b7329e..b41a36a78 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs | |||
@@ -89,7 +89,7 @@ pub use crate::{ | |||
89 | pub use { | 89 | pub use { |
90 | hir_def::{ | 90 | hir_def::{ |
91 | adt::StructKind, | 91 | adt::StructKind, |
92 | attr::{Attrs, Documentation}, | 92 | attr::{Attr, Attrs, Documentation}, |
93 | body::scope::ExprScopes, | 93 | body::scope::ExprScopes, |
94 | find_path::PrefixKind, | 94 | find_path::PrefixKind, |
95 | import_map, | 95 | import_map, |
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index 00b076175..15651bb22 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs | |||
@@ -574,7 +574,7 @@ impl<'db> SemanticsImpl<'db> { | |||
574 | } | 574 | } |
575 | 575 | ||
576 | fn scope_for_def(&self, def: Trait) -> SemanticsScope<'db> { | 576 | fn scope_for_def(&self, def: Trait) -> SemanticsScope<'db> { |
577 | let file_id = self.db.lookup_intern_trait(def.id).id.file_id; | 577 | let file_id = self.db.lookup_intern_trait(def.id).id.file_id(); |
578 | let resolver = def.id.resolver(self.db.upcast()); | 578 | let resolver = def.id.resolver(self.db.upcast()); |
579 | SemanticsScope { db: self.db, file_id, resolver } | 579 | SemanticsScope { db: self.db, file_id, resolver } |
580 | } | 580 | } |
@@ -752,6 +752,7 @@ macro_rules! to_def_impls { | |||
752 | 752 | ||
753 | to_def_impls![ | 753 | to_def_impls![ |
754 | (crate::Module, ast::Module, module_to_def), | 754 | (crate::Module, ast::Module, module_to_def), |
755 | (crate::Module, ast::SourceFile, source_file_to_def), | ||
755 | (crate::Struct, ast::Struct, struct_to_def), | 756 | (crate::Struct, ast::Struct, struct_to_def), |
756 | (crate::Enum, ast::Enum, enum_to_def), | 757 | (crate::Enum, ast::Enum, enum_to_def), |
757 | (crate::Union, ast::Union, union_to_def), | 758 | (crate::Union, ast::Union, union_to_def), |
diff --git a/crates/hir/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs index e9d820140..c6ad5ecb5 100644 --- a/crates/hir/src/semantics/source_to_def.rs +++ b/crates/hir/src/semantics/source_to_def.rs | |||
@@ -71,6 +71,12 @@ impl SourceToDefCtx<'_, '_> { | |||
71 | Some(def_map.module_id(child_id)) | 71 | Some(def_map.module_id(child_id)) |
72 | } | 72 | } |
73 | 73 | ||
74 | pub(super) fn source_file_to_def(&mut self, src: InFile<ast::SourceFile>) -> Option<ModuleId> { | ||
75 | let _p = profile::span("source_file_to_def"); | ||
76 | let file_id = src.file_id.original_file(self.db.upcast()); | ||
77 | self.file_to_def(file_id).get(0).copied() | ||
78 | } | ||
79 | |||
74 | pub(super) fn trait_to_def(&mut self, src: InFile<ast::Trait>) -> Option<TraitId> { | 80 | pub(super) fn trait_to_def(&mut self, src: InFile<ast::Trait>) -> Option<TraitId> { |
75 | self.to_def(src, keys::TRAIT) | 81 | self.to_def(src, keys::TRAIT) |
76 | } | 82 | } |
diff --git a/crates/hir_def/src/adt.rs b/crates/hir_def/src/adt.rs index 1b9bb8235..58e35353b 100644 --- a/crates/hir_def/src/adt.rs +++ b/crates/hir_def/src/adt.rs | |||
@@ -94,7 +94,7 @@ impl StructData { | |||
94 | pub(crate) fn struct_data_query(db: &dyn DefDatabase, id: StructId) -> Arc<StructData> { | 94 | pub(crate) fn struct_data_query(db: &dyn DefDatabase, id: StructId) -> Arc<StructData> { |
95 | let loc = id.lookup(db); | 95 | let loc = id.lookup(db); |
96 | let krate = loc.container.krate; | 96 | let krate = loc.container.krate; |
97 | let item_tree = db.item_tree(loc.id.file_id); | 97 | let item_tree = loc.id.item_tree(db); |
98 | let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into()); | 98 | let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into()); |
99 | let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone(); | 99 | let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone(); |
100 | 100 | ||
@@ -110,7 +110,7 @@ impl StructData { | |||
110 | pub(crate) fn union_data_query(db: &dyn DefDatabase, id: UnionId) -> Arc<StructData> { | 110 | pub(crate) fn union_data_query(db: &dyn DefDatabase, id: UnionId) -> Arc<StructData> { |
111 | let loc = id.lookup(db); | 111 | let loc = id.lookup(db); |
112 | let krate = loc.container.krate; | 112 | let krate = loc.container.krate; |
113 | let item_tree = db.item_tree(loc.id.file_id); | 113 | let item_tree = loc.id.item_tree(db); |
114 | let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into()); | 114 | let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into()); |
115 | let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone(); | 115 | let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone(); |
116 | 116 | ||
@@ -130,7 +130,7 @@ impl EnumData { | |||
130 | pub(crate) fn enum_data_query(db: &dyn DefDatabase, e: EnumId) -> Arc<EnumData> { | 130 | pub(crate) fn enum_data_query(db: &dyn DefDatabase, e: EnumId) -> Arc<EnumData> { |
131 | let loc = e.lookup(db); | 131 | let loc = e.lookup(db); |
132 | let krate = loc.container.krate; | 132 | let krate = loc.container.krate; |
133 | let item_tree = db.item_tree(loc.id.file_id); | 133 | let item_tree = loc.id.item_tree(db); |
134 | let cfg_options = db.crate_graph()[krate].cfg_options.clone(); | 134 | let cfg_options = db.crate_graph()[krate].cfg_options.clone(); |
135 | 135 | ||
136 | let enum_ = &item_tree[loc.id.value]; | 136 | let enum_ = &item_tree[loc.id.value]; |
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs index b0b4b5052..e4c84afbf 100644 --- a/crates/hir_def/src/attr.rs +++ b/crates/hir_def/src/attr.rs | |||
@@ -76,37 +76,23 @@ impl ops::Deref for Attrs { | |||
76 | impl RawAttrs { | 76 | impl RawAttrs { |
77 | pub(crate) const EMPTY: Self = Self { entries: None }; | 77 | pub(crate) const EMPTY: Self = Self { entries: None }; |
78 | 78 | ||
79 | pub(crate) fn new(owner: &dyn AttrsOwner, hygiene: &Hygiene) -> Self { | 79 | pub(crate) fn new(owner: &dyn ast::AttrsOwner, hygiene: &Hygiene) -> Self { |
80 | let attrs: Vec<_> = collect_attrs(owner).collect(); | 80 | let entries = collect_attrs(owner) |
81 | let entries = if attrs.is_empty() { | 81 | .enumerate() |
82 | // Avoid heap allocation | 82 | .flat_map(|(i, attr)| match attr { |
83 | None | 83 | Either::Left(attr) => Attr::from_src(attr, hygiene, i as u32), |
84 | } else { | 84 | Either::Right(comment) => comment.doc_comment().map(|doc| Attr { |
85 | Some( | 85 | index: i as u32, |
86 | attrs | 86 | input: Some(AttrInput::Literal(SmolStr::new(doc))), |
87 | .into_iter() | 87 | path: ModPath::from(hir_expand::name!(doc)), |
88 | .enumerate() | 88 | }), |
89 | .flat_map(|(i, attr)| match attr { | 89 | }) |
90 | Either::Left(attr) => Attr::from_src(attr, hygiene).map(|attr| (i, attr)), | 90 | .collect::<Arc<_>>(); |
91 | Either::Right(comment) => comment.doc_comment().map(|doc| { | 91 | |
92 | ( | 92 | Self { entries: if entries.is_empty() { None } else { Some(entries) } } |
93 | i, | ||
94 | Attr { | ||
95 | index: 0, | ||
96 | input: Some(AttrInput::Literal(SmolStr::new(doc))), | ||
97 | path: ModPath::from(hir_expand::name!(doc)), | ||
98 | }, | ||
99 | ) | ||
100 | }), | ||
101 | }) | ||
102 | .map(|(i, attr)| Attr { index: i as u32, ..attr }) | ||
103 | .collect(), | ||
104 | ) | ||
105 | }; | ||
106 | Self { entries } | ||
107 | } | 93 | } |
108 | 94 | ||
109 | fn from_attrs_owner(db: &dyn DefDatabase, owner: InFile<&dyn AttrsOwner>) -> Self { | 95 | fn from_attrs_owner(db: &dyn DefDatabase, owner: InFile<&dyn ast::AttrsOwner>) -> Self { |
110 | let hygiene = Hygiene::new(db.upcast(), owner.file_id); | 96 | let hygiene = Hygiene::new(db.upcast(), owner.file_id); |
111 | Self::new(owner.value, &hygiene) | 97 | Self::new(owner.value, &hygiene) |
112 | } | 98 | } |
@@ -136,16 +122,15 @@ impl RawAttrs { | |||
136 | let new_attrs = self | 122 | let new_attrs = self |
137 | .iter() | 123 | .iter() |
138 | .flat_map(|attr| -> SmallVec<[_; 1]> { | 124 | .flat_map(|attr| -> SmallVec<[_; 1]> { |
139 | let attr = attr.clone(); | ||
140 | let is_cfg_attr = | 125 | let is_cfg_attr = |
141 | attr.path.as_ident().map_or(false, |name| *name == hir_expand::name![cfg_attr]); | 126 | attr.path.as_ident().map_or(false, |name| *name == hir_expand::name![cfg_attr]); |
142 | if !is_cfg_attr { | 127 | if !is_cfg_attr { |
143 | return smallvec![attr]; | 128 | return smallvec![attr.clone()]; |
144 | } | 129 | } |
145 | 130 | ||
146 | let subtree = match &attr.input { | 131 | let subtree = match &attr.input { |
147 | Some(AttrInput::TokenTree(it)) => it, | 132 | Some(AttrInput::TokenTree(it)) => it, |
148 | _ => return smallvec![attr], | 133 | _ => return smallvec![attr.clone()], |
149 | }; | 134 | }; |
150 | 135 | ||
151 | // Input subtree is: `(cfg, $(attr),+)` | 136 | // Input subtree is: `(cfg, $(attr),+)` |
@@ -157,11 +142,13 @@ impl RawAttrs { | |||
157 | let cfg = parts.next().unwrap(); | 142 | let cfg = parts.next().unwrap(); |
158 | let cfg = Subtree { delimiter: subtree.delimiter, token_trees: cfg.to_vec() }; | 143 | let cfg = Subtree { delimiter: subtree.delimiter, token_trees: cfg.to_vec() }; |
159 | let cfg = CfgExpr::parse(&cfg); | 144 | let cfg = CfgExpr::parse(&cfg); |
145 | let index = attr.index; | ||
160 | let attrs = parts.filter(|a| !a.is_empty()).filter_map(|attr| { | 146 | let attrs = parts.filter(|a| !a.is_empty()).filter_map(|attr| { |
161 | let tree = Subtree { delimiter: None, token_trees: attr.to_vec() }; | 147 | let tree = Subtree { delimiter: None, token_trees: attr.to_vec() }; |
162 | let attr = ast::Attr::parse(&format!("#[{}]", tree)).ok()?; | 148 | let attr = ast::Attr::parse(&format!("#[{}]", tree)).ok()?; |
163 | let hygiene = Hygiene::new_unhygienic(); // FIXME | 149 | // FIXME hygiene |
164 | Attr::from_src(attr, &hygiene) | 150 | let hygiene = Hygiene::new_unhygienic(); |
151 | Attr::from_src(attr, &hygiene, index) | ||
165 | }); | 152 | }); |
166 | 153 | ||
167 | let cfg_options = &crate_graph[krate].cfg_options; | 154 | let cfg_options = &crate_graph[krate].cfg_options; |
@@ -191,7 +178,7 @@ impl Attrs { | |||
191 | Some(it) => { | 178 | Some(it) => { |
192 | let raw_attrs = RawAttrs::from_attrs_owner( | 179 | let raw_attrs = RawAttrs::from_attrs_owner( |
193 | db, | 180 | db, |
194 | it.as_ref().map(|it| it as &dyn AttrsOwner), | 181 | it.as_ref().map(|it| it as &dyn ast::AttrsOwner), |
195 | ); | 182 | ); |
196 | match mod_data.definition_source(db) { | 183 | match mod_data.definition_source(db) { |
197 | InFile { file_id, value: ModuleSource::SourceFile(file) } => raw_attrs | 184 | InFile { file_id, value: ModuleSource::SourceFile(file) } => raw_attrs |
@@ -202,9 +189,9 @@ impl Attrs { | |||
202 | None => RawAttrs::from_attrs_owner( | 189 | None => RawAttrs::from_attrs_owner( |
203 | db, | 190 | db, |
204 | mod_data.definition_source(db).as_ref().map(|src| match src { | 191 | mod_data.definition_source(db).as_ref().map(|src| match src { |
205 | ModuleSource::SourceFile(file) => file as &dyn AttrsOwner, | 192 | ModuleSource::SourceFile(file) => file as &dyn ast::AttrsOwner, |
206 | ModuleSource::Module(module) => module as &dyn AttrsOwner, | 193 | ModuleSource::Module(module) => module as &dyn ast::AttrsOwner, |
207 | ModuleSource::BlockExpr(block) => block as &dyn AttrsOwner, | 194 | ModuleSource::BlockExpr(block) => block as &dyn ast::AttrsOwner, |
208 | }), | 195 | }), |
209 | ), | 196 | ), |
210 | } | 197 | } |
@@ -262,7 +249,7 @@ impl Attrs { | |||
262 | let mut res = ArenaMap::default(); | 249 | let mut res = ArenaMap::default(); |
263 | 250 | ||
264 | for (id, var) in src.value.iter() { | 251 | for (id, var) in src.value.iter() { |
265 | let attrs = RawAttrs::from_attrs_owner(db, src.with_value(var as &dyn AttrsOwner)) | 252 | let attrs = RawAttrs::from_attrs_owner(db, src.with_value(var as &dyn ast::AttrsOwner)) |
266 | .filter(db, krate); | 253 | .filter(db, krate); |
267 | 254 | ||
268 | res.insert(id, attrs) | 255 | res.insert(id, attrs) |
@@ -293,6 +280,13 @@ impl Attrs { | |||
293 | Arc::new(res) | 280 | Arc::new(res) |
294 | } | 281 | } |
295 | 282 | ||
283 | /// Constructs a map that maps the lowered `Attr`s in this `Attrs` back to its original syntax nodes. | ||
284 | /// | ||
285 | /// `owner` must be the original owner of the attributes. | ||
286 | pub fn source_map(&self, owner: &dyn ast::AttrsOwner) -> AttrSourceMap { | ||
287 | AttrSourceMap { attrs: collect_attrs(owner).collect() } | ||
288 | } | ||
289 | |||
296 | pub fn by_key(&self, key: &'static str) -> AttrQuery<'_> { | 290 | pub fn by_key(&self, key: &'static str) -> AttrQuery<'_> { |
297 | AttrQuery { attrs: self, key } | 291 | AttrQuery { attrs: self, key } |
298 | } | 292 | } |
@@ -317,15 +311,34 @@ impl Attrs { | |||
317 | AttrInput::Literal(s) => Some(s), | 311 | AttrInput::Literal(s) => Some(s), |
318 | AttrInput::TokenTree(_) => None, | 312 | AttrInput::TokenTree(_) => None, |
319 | }); | 313 | }); |
320 | // FIXME: Replace `Itertools::intersperse` with `Iterator::intersperse[_with]` until the | 314 | let indent = docs |
321 | // libstd api gets stabilized (https://github.com/rust-lang/rust/issues/79524). | 315 | .clone() |
322 | let docs = Itertools::intersperse(docs, &SmolStr::new_inline("\n")) | 316 | .flat_map(|s| s.lines()) |
323 | .map(|it| it.as_str()) | 317 | .filter(|line| !line.chars().all(|c| c.is_whitespace())) |
324 | .collect::<String>(); | 318 | .map(|line| line.chars().take_while(|c| c.is_whitespace()).count()) |
325 | if docs.is_empty() { | 319 | .min() |
320 | .unwrap_or(0); | ||
321 | let mut buf = String::new(); | ||
322 | for doc in docs { | ||
323 | // str::lines doesn't yield anything for the empty string | ||
324 | if !doc.is_empty() { | ||
325 | buf.extend(Itertools::intersperse( | ||
326 | doc.lines().map(|line| { | ||
327 | line.char_indices() | ||
328 | .nth(indent) | ||
329 | .map_or(line, |(offset, _)| &line[offset..]) | ||
330 | .trim_end() | ||
331 | }), | ||
332 | "\n", | ||
333 | )); | ||
334 | } | ||
335 | buf.push('\n'); | ||
336 | } | ||
337 | buf.pop(); | ||
338 | if buf.is_empty() { | ||
326 | None | 339 | None |
327 | } else { | 340 | } else { |
328 | Some(Documentation(docs)) | 341 | Some(Documentation(buf)) |
329 | } | 342 | } |
330 | } | 343 | } |
331 | } | 344 | } |
@@ -365,6 +378,24 @@ fn inner_attributes( | |||
365 | Some((attrs, docs)) | 378 | Some((attrs, docs)) |
366 | } | 379 | } |
367 | 380 | ||
381 | pub struct AttrSourceMap { | ||
382 | attrs: Vec<Either<ast::Attr, ast::Comment>>, | ||
383 | } | ||
384 | |||
385 | impl AttrSourceMap { | ||
386 | /// Maps the lowered `Attr` back to its original syntax node. | ||
387 | /// | ||
388 | /// `attr` must come from the `owner` used for AttrSourceMap | ||
389 | /// | ||
390 | /// Note that the returned syntax node might be a `#[cfg_attr]`, or a doc comment, instead of | ||
391 | /// the attribute represented by `Attr`. | ||
392 | pub fn source_of(&self, attr: &Attr) -> &Either<ast::Attr, ast::Comment> { | ||
393 | self.attrs | ||
394 | .get(attr.index as usize) | ||
395 | .unwrap_or_else(|| panic!("cannot find `Attr` at index {}", attr.index)) | ||
396 | } | ||
397 | } | ||
398 | |||
368 | #[derive(Debug, Clone, PartialEq, Eq)] | 399 | #[derive(Debug, Clone, PartialEq, Eq)] |
369 | pub struct Attr { | 400 | pub struct Attr { |
370 | index: u32, | 401 | index: u32, |
@@ -381,7 +412,7 @@ pub enum AttrInput { | |||
381 | } | 412 | } |
382 | 413 | ||
383 | impl Attr { | 414 | impl Attr { |
384 | fn from_src(ast: ast::Attr, hygiene: &Hygiene) -> Option<Attr> { | 415 | fn from_src(ast: ast::Attr, hygiene: &Hygiene, index: u32) -> Option<Attr> { |
385 | let path = ModPath::from_src(ast.path()?, hygiene)?; | 416 | let path = ModPath::from_src(ast.path()?, hygiene)?; |
386 | let input = if let Some(lit) = ast.literal() { | 417 | let input = if let Some(lit) = ast.literal() { |
387 | let value = match lit.kind() { | 418 | let value = match lit.kind() { |
@@ -394,7 +425,7 @@ impl Attr { | |||
394 | } else { | 425 | } else { |
395 | None | 426 | None |
396 | }; | 427 | }; |
397 | Some(Attr { index: 0, path, input }) | 428 | Some(Attr { index, path, input }) |
398 | } | 429 | } |
399 | 430 | ||
400 | /// Maps this lowered `Attr` back to its original syntax node. | 431 | /// Maps this lowered `Attr` back to its original syntax node. |
@@ -403,7 +434,7 @@ impl Attr { | |||
403 | /// | 434 | /// |
404 | /// Note that the returned syntax node might be a `#[cfg_attr]`, or a doc comment, instead of | 435 | /// Note that the returned syntax node might be a `#[cfg_attr]`, or a doc comment, instead of |
405 | /// the attribute represented by `Attr`. | 436 | /// the attribute represented by `Attr`. |
406 | pub fn to_src(&self, owner: &dyn AttrsOwner) -> Either<ast::Attr, ast::Comment> { | 437 | pub fn to_src(&self, owner: &dyn ast::AttrsOwner) -> Either<ast::Attr, ast::Comment> { |
407 | collect_attrs(owner).nth(self.index as usize).unwrap_or_else(|| { | 438 | collect_attrs(owner).nth(self.index as usize).unwrap_or_else(|| { |
408 | panic!("cannot find `Attr` at index {} in {}", self.index, owner.syntax()) | 439 | panic!("cannot find `Attr` at index {} in {}", self.index, owner.syntax()) |
409 | }) | 440 | }) |
@@ -448,6 +479,13 @@ impl Attr { | |||
448 | _ => None, | 479 | _ => None, |
449 | } | 480 | } |
450 | } | 481 | } |
482 | |||
483 | pub fn string_value(&self) -> Option<&SmolStr> { | ||
484 | match self.input.as_ref()? { | ||
485 | AttrInput::Literal(it) => Some(it), | ||
486 | _ => None, | ||
487 | } | ||
488 | } | ||
451 | } | 489 | } |
452 | 490 | ||
453 | #[derive(Debug, Clone, Copy)] | 491 | #[derive(Debug, Clone, Copy)] |
@@ -475,7 +513,7 @@ impl<'a> AttrQuery<'a> { | |||
475 | self.attrs().next().is_some() | 513 | self.attrs().next().is_some() |
476 | } | 514 | } |
477 | 515 | ||
478 | pub(crate) fn attrs(self) -> impl Iterator<Item = &'a Attr> { | 516 | pub fn attrs(self) -> impl Iterator<Item = &'a Attr> + Clone { |
479 | let key = self.key; | 517 | let key = self.key; |
480 | self.attrs | 518 | self.attrs |
481 | .iter() | 519 | .iter() |
@@ -488,16 +526,18 @@ where | |||
488 | N: ast::AttrsOwner, | 526 | N: ast::AttrsOwner, |
489 | { | 527 | { |
490 | let src = InFile::new(src.file_id, src.to_node(db.upcast())); | 528 | let src = InFile::new(src.file_id, src.to_node(db.upcast())); |
491 | RawAttrs::from_attrs_owner(db, src.as_ref().map(|it| it as &dyn AttrsOwner)) | 529 | RawAttrs::from_attrs_owner(db, src.as_ref().map(|it| it as &dyn ast::AttrsOwner)) |
492 | } | 530 | } |
493 | 531 | ||
494 | fn attrs_from_item_tree<N: ItemTreeNode>(id: ItemTreeId<N>, db: &dyn DefDatabase) -> RawAttrs { | 532 | fn attrs_from_item_tree<N: ItemTreeNode>(id: ItemTreeId<N>, db: &dyn DefDatabase) -> RawAttrs { |
495 | let tree = db.item_tree(id.file_id); | 533 | let tree = id.item_tree(db); |
496 | let mod_item = N::id_to_mod_item(id.value); | 534 | let mod_item = N::id_to_mod_item(id.value); |
497 | tree.raw_attrs(mod_item.into()).clone() | 535 | tree.raw_attrs(mod_item.into()).clone() |
498 | } | 536 | } |
499 | 537 | ||
500 | fn collect_attrs(owner: &dyn AttrsOwner) -> impl Iterator<Item = Either<ast::Attr, ast::Comment>> { | 538 | fn collect_attrs( |
539 | owner: &dyn ast::AttrsOwner, | ||
540 | ) -> impl Iterator<Item = Either<ast::Attr, ast::Comment>> { | ||
501 | let (inner_attrs, inner_docs) = inner_attributes(owner.syntax()) | 541 | let (inner_attrs, inner_docs) = inner_attributes(owner.syntax()) |
502 | .map_or((None, None), |(attrs, docs)| ((Some(attrs), Some(docs)))); | 542 | .map_or((None, None), |(attrs, docs)| ((Some(attrs), Some(docs)))); |
503 | 543 | ||
diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs index 60b25db56..19f5065d1 100644 --- a/crates/hir_def/src/body/lower.rs +++ b/crates/hir_def/src/body/lower.rs | |||
@@ -177,12 +177,15 @@ impl ExprCollector<'_> { | |||
177 | } | 177 | } |
178 | 178 | ||
179 | fn collect_expr(&mut self, expr: ast::Expr) -> ExprId { | 179 | fn collect_expr(&mut self, expr: ast::Expr) -> ExprId { |
180 | self.maybe_collect_expr(expr).unwrap_or_else(|| self.missing_expr()) | ||
181 | } | ||
182 | |||
183 | /// Returns `None` if the expression is `#[cfg]`d out. | ||
184 | fn maybe_collect_expr(&mut self, expr: ast::Expr) -> Option<ExprId> { | ||
180 | let syntax_ptr = AstPtr::new(&expr); | 185 | let syntax_ptr = AstPtr::new(&expr); |
181 | if self.check_cfg(&expr).is_none() { | 186 | self.check_cfg(&expr)?; |
182 | return self.missing_expr(); | ||
183 | } | ||
184 | 187 | ||
185 | match expr { | 188 | Some(match expr { |
186 | ast::Expr::IfExpr(e) => { | 189 | ast::Expr::IfExpr(e) => { |
187 | let then_branch = self.collect_block_opt(e.then_branch()); | 190 | let then_branch = self.collect_block_opt(e.then_branch()); |
188 | 191 | ||
@@ -211,8 +214,9 @@ impl ExprCollector<'_> { | |||
211 | guard: None, | 214 | guard: None, |
212 | }, | 215 | }, |
213 | ]; | 216 | ]; |
214 | return self | 217 | return Some( |
215 | .alloc_expr(Expr::Match { expr: match_expr, arms }, syntax_ptr); | 218 | self.alloc_expr(Expr::Match { expr: match_expr, arms }, syntax_ptr), |
219 | ); | ||
216 | } | 220 | } |
217 | }, | 221 | }, |
218 | }; | 222 | }; |
@@ -283,8 +287,9 @@ impl ExprCollector<'_> { | |||
283 | ]; | 287 | ]; |
284 | let match_expr = | 288 | let match_expr = |
285 | self.alloc_expr_desugared(Expr::Match { expr: match_expr, arms }); | 289 | self.alloc_expr_desugared(Expr::Match { expr: match_expr, arms }); |
286 | return self | 290 | return Some( |
287 | .alloc_expr(Expr::Loop { body: match_expr, label }, syntax_ptr); | 291 | self.alloc_expr(Expr::Loop { body: match_expr, label }, syntax_ptr), |
292 | ); | ||
288 | } | 293 | } |
289 | }, | 294 | }, |
290 | }; | 295 | }; |
@@ -301,7 +306,7 @@ impl ExprCollector<'_> { | |||
301 | ast::Expr::CallExpr(e) => { | 306 | ast::Expr::CallExpr(e) => { |
302 | let callee = self.collect_expr_opt(e.expr()); | 307 | let callee = self.collect_expr_opt(e.expr()); |
303 | let args = if let Some(arg_list) = e.arg_list() { | 308 | let args = if let Some(arg_list) = e.arg_list() { |
304 | arg_list.args().map(|e| self.collect_expr(e)).collect() | 309 | arg_list.args().filter_map(|e| self.maybe_collect_expr(e)).collect() |
305 | } else { | 310 | } else { |
306 | Vec::new() | 311 | Vec::new() |
307 | }; | 312 | }; |
@@ -310,7 +315,7 @@ impl ExprCollector<'_> { | |||
310 | ast::Expr::MethodCallExpr(e) => { | 315 | ast::Expr::MethodCallExpr(e) => { |
311 | let receiver = self.collect_expr_opt(e.receiver()); | 316 | let receiver = self.collect_expr_opt(e.receiver()); |
312 | let args = if let Some(arg_list) = e.arg_list() { | 317 | let args = if let Some(arg_list) = e.arg_list() { |
313 | arg_list.args().map(|e| self.collect_expr(e)).collect() | 318 | arg_list.args().filter_map(|e| self.maybe_collect_expr(e)).collect() |
314 | } else { | 319 | } else { |
315 | Vec::new() | 320 | Vec::new() |
316 | }; | 321 | }; |
@@ -538,7 +543,7 @@ impl ExprCollector<'_> { | |||
538 | self.alloc_expr(Expr::Missing, syntax_ptr) | 543 | self.alloc_expr(Expr::Missing, syntax_ptr) |
539 | } | 544 | } |
540 | } | 545 | } |
541 | } | 546 | }) |
542 | } | 547 | } |
543 | 548 | ||
544 | fn collect_macro_call<F: FnMut(&mut Self, Option<T>), T: ast::AstNode>( | 549 | fn collect_macro_call<F: FnMut(&mut Self, Option<T>), T: ast::AstNode>( |
diff --git a/crates/hir_def/src/body/tests.rs b/crates/hir_def/src/body/tests.rs index f8e6f70e8..faa133297 100644 --- a/crates/hir_def/src/body/tests.rs +++ b/crates/hir_def/src/body/tests.rs | |||
@@ -137,7 +137,7 @@ fn f() { | |||
137 | include!(invalid); | 137 | include!(invalid); |
138 | //^^^^^^^^^^^^^^^^^ could not convert tokens | 138 | //^^^^^^^^^^^^^^^^^ could not convert tokens |
139 | include!("does not exist"); | 139 | include!("does not exist"); |
140 | //^^^^^^^^^^^^^^^^^^^^^^^^^^ could not convert tokens | 140 | //^^^^^^^^^^^^^^^^^^^^^^^^^^ failed to load file `does not exist` |
141 | 141 | ||
142 | env!(invalid); | 142 | env!(invalid); |
143 | //^^^^^^^^^^^^^ could not convert tokens | 143 | //^^^^^^^^^^^^^ could not convert tokens |
diff --git a/crates/hir_def/src/data.rs b/crates/hir_def/src/data.rs index 1a27f7bf2..0be868ba2 100644 --- a/crates/hir_def/src/data.rs +++ b/crates/hir_def/src/data.rs | |||
@@ -9,7 +9,7 @@ use crate::{ | |||
9 | attr::Attrs, | 9 | attr::Attrs, |
10 | body::Expander, | 10 | body::Expander, |
11 | db::DefDatabase, | 11 | db::DefDatabase, |
12 | item_tree::{AssocItem, FunctionQualifier, ItemTreeId, ModItem}, | 12 | item_tree::{AssocItem, FunctionQualifier, ItemTreeId, ModItem, Param}, |
13 | type_ref::{TypeBound, TypeRef}, | 13 | type_ref::{TypeBound, TypeRef}, |
14 | visibility::RawVisibility, | 14 | visibility::RawVisibility, |
15 | AssocContainerId, AssocItemId, ConstId, ConstLoc, FunctionId, FunctionLoc, HasModule, ImplId, | 15 | AssocContainerId, AssocItemId, ConstId, ConstLoc, FunctionId, FunctionLoc, HasModule, ImplId, |
@@ -36,19 +36,38 @@ impl FunctionData { | |||
36 | pub(crate) fn fn_data_query(db: &dyn DefDatabase, func: FunctionId) -> Arc<FunctionData> { | 36 | pub(crate) fn fn_data_query(db: &dyn DefDatabase, func: FunctionId) -> Arc<FunctionData> { |
37 | let loc = func.lookup(db); | 37 | let loc = func.lookup(db); |
38 | let krate = loc.container.module(db).krate; | 38 | let krate = loc.container.module(db).krate; |
39 | let item_tree = db.item_tree(loc.id.file_id); | 39 | let crate_graph = db.crate_graph(); |
40 | let cfg_options = &crate_graph[krate].cfg_options; | ||
41 | let item_tree = loc.id.item_tree(db); | ||
40 | let func = &item_tree[loc.id.value]; | 42 | let func = &item_tree[loc.id.value]; |
41 | 43 | ||
44 | let enabled_params = func | ||
45 | .params | ||
46 | .clone() | ||
47 | .filter(|¶m| item_tree.attrs(db, krate, param.into()).is_cfg_enabled(cfg_options)); | ||
48 | |||
49 | // If last cfg-enabled param is a `...` param, it's a varargs function. | ||
50 | let is_varargs = enabled_params | ||
51 | .clone() | ||
52 | .next_back() | ||
53 | .map_or(false, |param| matches!(item_tree[param], Param::Varargs)); | ||
54 | |||
42 | Arc::new(FunctionData { | 55 | Arc::new(FunctionData { |
43 | name: func.name.clone(), | 56 | name: func.name.clone(), |
44 | params: func.params.iter().map(|id| item_tree[*id].clone()).collect(), | 57 | params: enabled_params |
58 | .clone() | ||
59 | .filter_map(|id| match &item_tree[id] { | ||
60 | Param::Normal(ty) => Some(item_tree[*ty].clone()), | ||
61 | Param::Varargs => None, | ||
62 | }) | ||
63 | .collect(), | ||
45 | ret_type: item_tree[func.ret_type].clone(), | 64 | ret_type: item_tree[func.ret_type].clone(), |
46 | attrs: item_tree.attrs(db, krate, ModItem::from(loc.id.value).into()), | 65 | attrs: item_tree.attrs(db, krate, ModItem::from(loc.id.value).into()), |
47 | has_self_param: func.has_self_param, | 66 | has_self_param: func.has_self_param, |
48 | has_body: func.has_body, | 67 | has_body: func.has_body, |
49 | qualifier: func.qualifier.clone(), | 68 | qualifier: func.qualifier.clone(), |
50 | is_in_extern_block: func.is_in_extern_block, | 69 | is_in_extern_block: func.is_in_extern_block, |
51 | is_varargs: func.is_varargs, | 70 | is_varargs, |
52 | visibility: item_tree[func.visibility].clone(), | 71 | visibility: item_tree[func.visibility].clone(), |
53 | }) | 72 | }) |
54 | } | 73 | } |
@@ -70,7 +89,7 @@ impl TypeAliasData { | |||
70 | typ: TypeAliasId, | 89 | typ: TypeAliasId, |
71 | ) -> Arc<TypeAliasData> { | 90 | ) -> Arc<TypeAliasData> { |
72 | let loc = typ.lookup(db); | 91 | let loc = typ.lookup(db); |
73 | let item_tree = db.item_tree(loc.id.file_id); | 92 | let item_tree = loc.id.item_tree(db); |
74 | let typ = &item_tree[loc.id.value]; | 93 | let typ = &item_tree[loc.id.value]; |
75 | 94 | ||
76 | Arc::new(TypeAliasData { | 95 | Arc::new(TypeAliasData { |
@@ -96,23 +115,23 @@ pub struct TraitData { | |||
96 | impl TraitData { | 115 | impl TraitData { |
97 | pub(crate) fn trait_data_query(db: &dyn DefDatabase, tr: TraitId) -> Arc<TraitData> { | 116 | pub(crate) fn trait_data_query(db: &dyn DefDatabase, tr: TraitId) -> Arc<TraitData> { |
98 | let tr_loc = tr.lookup(db); | 117 | let tr_loc = tr.lookup(db); |
99 | let item_tree = db.item_tree(tr_loc.id.file_id); | 118 | let item_tree = tr_loc.id.item_tree(db); |
100 | let tr_def = &item_tree[tr_loc.id.value]; | 119 | let tr_def = &item_tree[tr_loc.id.value]; |
101 | let name = tr_def.name.clone(); | 120 | let name = tr_def.name.clone(); |
102 | let is_auto = tr_def.is_auto; | 121 | let is_auto = tr_def.is_auto; |
103 | let is_unsafe = tr_def.is_unsafe; | 122 | let is_unsafe = tr_def.is_unsafe; |
104 | let module_id = tr_loc.container; | 123 | let module_id = tr_loc.container; |
105 | let container = AssocContainerId::TraitId(tr); | 124 | let container = AssocContainerId::TraitId(tr); |
106 | let mut expander = Expander::new(db, tr_loc.id.file_id, module_id); | ||
107 | let visibility = item_tree[tr_def.visibility].clone(); | 125 | let visibility = item_tree[tr_def.visibility].clone(); |
108 | let bounds = tr_def.bounds.clone(); | 126 | let bounds = tr_def.bounds.clone(); |
127 | let mut expander = Expander::new(db, tr_loc.id.file_id(), module_id); | ||
109 | 128 | ||
110 | let items = collect_items( | 129 | let items = collect_items( |
111 | db, | 130 | db, |
112 | module_id, | 131 | module_id, |
113 | &mut expander, | 132 | &mut expander, |
114 | tr_def.items.iter().copied(), | 133 | tr_def.items.iter().copied(), |
115 | tr_loc.id.file_id, | 134 | tr_loc.id.file_id(), |
116 | container, | 135 | container, |
117 | 100, | 136 | 100, |
118 | ); | 137 | ); |
@@ -148,21 +167,21 @@ impl ImplData { | |||
148 | let _p = profile::span("impl_data_query"); | 167 | let _p = profile::span("impl_data_query"); |
149 | let impl_loc = id.lookup(db); | 168 | let impl_loc = id.lookup(db); |
150 | 169 | ||
151 | let item_tree = db.item_tree(impl_loc.id.file_id); | 170 | let item_tree = impl_loc.id.item_tree(db); |
152 | let impl_def = &item_tree[impl_loc.id.value]; | 171 | let impl_def = &item_tree[impl_loc.id.value]; |
153 | let target_trait = impl_def.target_trait.map(|id| item_tree[id].clone()); | 172 | let target_trait = impl_def.target_trait.map(|id| item_tree[id].clone()); |
154 | let target_type = item_tree[impl_def.target_type].clone(); | 173 | let target_type = item_tree[impl_def.target_type].clone(); |
155 | let is_negative = impl_def.is_negative; | 174 | let is_negative = impl_def.is_negative; |
156 | let module_id = impl_loc.container; | 175 | let module_id = impl_loc.container; |
157 | let container = AssocContainerId::ImplId(id); | 176 | let container = AssocContainerId::ImplId(id); |
158 | let mut expander = Expander::new(db, impl_loc.id.file_id, module_id); | 177 | let mut expander = Expander::new(db, impl_loc.id.file_id(), module_id); |
159 | 178 | ||
160 | let items = collect_items( | 179 | let items = collect_items( |
161 | db, | 180 | db, |
162 | module_id, | 181 | module_id, |
163 | &mut expander, | 182 | &mut expander, |
164 | impl_def.items.iter().copied(), | 183 | impl_def.items.iter().copied(), |
165 | impl_loc.id.file_id, | 184 | impl_loc.id.file_id(), |
166 | container, | 185 | container, |
167 | 100, | 186 | 100, |
168 | ); | 187 | ); |
@@ -183,7 +202,7 @@ pub struct ConstData { | |||
183 | impl ConstData { | 202 | impl ConstData { |
184 | pub(crate) fn const_data_query(db: &dyn DefDatabase, konst: ConstId) -> Arc<ConstData> { | 203 | pub(crate) fn const_data_query(db: &dyn DefDatabase, konst: ConstId) -> Arc<ConstData> { |
185 | let loc = konst.lookup(db); | 204 | let loc = konst.lookup(db); |
186 | let item_tree = db.item_tree(loc.id.file_id); | 205 | let item_tree = loc.id.item_tree(db); |
187 | let konst = &item_tree[loc.id.value]; | 206 | let konst = &item_tree[loc.id.value]; |
188 | 207 | ||
189 | Arc::new(ConstData { | 208 | Arc::new(ConstData { |
@@ -206,7 +225,7 @@ pub struct StaticData { | |||
206 | impl StaticData { | 225 | impl StaticData { |
207 | pub(crate) fn static_data_query(db: &dyn DefDatabase, konst: StaticId) -> Arc<StaticData> { | 226 | pub(crate) fn static_data_query(db: &dyn DefDatabase, konst: StaticId) -> Arc<StaticData> { |
208 | let node = konst.lookup(db); | 227 | let node = konst.lookup(db); |
209 | let item_tree = db.item_tree(node.id.file_id); | 228 | let item_tree = node.id.item_tree(db); |
210 | let statik = &item_tree[node.id.value]; | 229 | let statik = &item_tree[node.id.value]; |
211 | 230 | ||
212 | Arc::new(StaticData { | 231 | Arc::new(StaticData { |
@@ -232,22 +251,23 @@ fn collect_items( | |||
232 | return Vec::new(); | 251 | return Vec::new(); |
233 | } | 252 | } |
234 | 253 | ||
235 | let item_tree = db.item_tree(file_id); | 254 | let item_tree = db.file_item_tree(file_id); |
236 | let cfg_options = db.crate_graph()[module.krate].cfg_options.clone(); | 255 | let crate_graph = db.crate_graph(); |
256 | let cfg_options = &crate_graph[module.krate].cfg_options; | ||
237 | 257 | ||
238 | let mut items = Vec::new(); | 258 | let mut items = Vec::new(); |
239 | for item in assoc_items { | 259 | for item in assoc_items { |
260 | let attrs = item_tree.attrs(db, module.krate, ModItem::from(item).into()); | ||
261 | if !attrs.is_cfg_enabled(cfg_options) { | ||
262 | continue; | ||
263 | } | ||
264 | |||
240 | match item { | 265 | match item { |
241 | AssocItem::Function(id) => { | 266 | AssocItem::Function(id) => { |
242 | let item = &item_tree[id]; | 267 | let item = &item_tree[id]; |
243 | let attrs = item_tree.attrs(db, module.krate, ModItem::from(id).into()); | ||
244 | if !attrs.is_cfg_enabled(&cfg_options) { | ||
245 | continue; | ||
246 | } | ||
247 | let def = FunctionLoc { container, id: ItemTreeId::new(file_id, id) }.intern(db); | 268 | let def = FunctionLoc { container, id: ItemTreeId::new(file_id, id) }.intern(db); |
248 | items.push((item.name.clone(), def.into())); | 269 | items.push((item.name.clone(), def.into())); |
249 | } | 270 | } |
250 | // FIXME: cfg? | ||
251 | AssocItem::Const(id) => { | 271 | AssocItem::Const(id) => { |
252 | let item = &item_tree[id]; | 272 | let item = &item_tree[id]; |
253 | let name = match item.name.clone() { | 273 | let name = match item.name.clone() { |
@@ -272,7 +292,7 @@ fn collect_items( | |||
272 | if let Ok(res) = res { | 292 | if let Ok(res) = res { |
273 | if let Some((mark, mac)) = res.value { | 293 | if let Some((mark, mac)) = res.value { |
274 | let src: InFile<ast::MacroItems> = expander.to_source(mac); | 294 | let src: InFile<ast::MacroItems> = expander.to_source(mac); |
275 | let item_tree = db.item_tree(src.file_id); | 295 | let item_tree = db.file_item_tree(src.file_id); |
276 | let iter = | 296 | let iter = |
277 | item_tree.top_level_items().iter().filter_map(ModItem::as_assoc_item); | 297 | item_tree.top_level_items().iter().filter_map(ModItem::as_assoc_item); |
278 | items.extend(collect_items( | 298 | items.extend(collect_items( |
diff --git a/crates/hir_def/src/db.rs b/crates/hir_def/src/db.rs index cca5a086b..276caf5b3 100644 --- a/crates/hir_def/src/db.rs +++ b/crates/hir_def/src/db.rs | |||
@@ -48,8 +48,8 @@ pub trait InternDatabase: SourceDatabase { | |||
48 | 48 | ||
49 | #[salsa::query_group(DefDatabaseStorage)] | 49 | #[salsa::query_group(DefDatabaseStorage)] |
50 | pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> { | 50 | pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> { |
51 | #[salsa::invoke(ItemTree::item_tree_query)] | 51 | #[salsa::invoke(ItemTree::file_item_tree_query)] |
52 | fn item_tree(&self, file_id: HirFileId) -> Arc<ItemTree>; | 52 | fn file_item_tree(&self, file_id: HirFileId) -> Arc<ItemTree>; |
53 | 53 | ||
54 | #[salsa::invoke(crate_def_map_wait)] | 54 | #[salsa::invoke(crate_def_map_wait)] |
55 | #[salsa::transparent] | 55 | #[salsa::transparent] |
diff --git a/crates/hir_def/src/generics.rs b/crates/hir_def/src/generics.rs index a056ab797..7c6cbff11 100644 --- a/crates/hir_def/src/generics.rs +++ b/crates/hir_def/src/generics.rs | |||
@@ -97,43 +97,43 @@ impl GenericParams { | |||
97 | let generics = match def { | 97 | let generics = match def { |
98 | GenericDefId::FunctionId(id) => { | 98 | GenericDefId::FunctionId(id) => { |
99 | let id = id.lookup(db).id; | 99 | let id = id.lookup(db).id; |
100 | let tree = db.item_tree(id.file_id); | 100 | let tree = id.item_tree(db); |
101 | let item = &tree[id.value]; | 101 | let item = &tree[id.value]; |
102 | tree[item.generic_params].clone() | 102 | tree[item.generic_params].clone() |
103 | } | 103 | } |
104 | GenericDefId::AdtId(AdtId::StructId(id)) => { | 104 | GenericDefId::AdtId(AdtId::StructId(id)) => { |
105 | let id = id.lookup(db).id; | 105 | let id = id.lookup(db).id; |
106 | let tree = db.item_tree(id.file_id); | 106 | let tree = id.item_tree(db); |
107 | let item = &tree[id.value]; | 107 | let item = &tree[id.value]; |
108 | tree[item.generic_params].clone() | 108 | tree[item.generic_params].clone() |
109 | } | 109 | } |
110 | GenericDefId::AdtId(AdtId::EnumId(id)) => { | 110 | GenericDefId::AdtId(AdtId::EnumId(id)) => { |
111 | let id = id.lookup(db).id; | 111 | let id = id.lookup(db).id; |
112 | let tree = db.item_tree(id.file_id); | 112 | let tree = id.item_tree(db); |
113 | let item = &tree[id.value]; | 113 | let item = &tree[id.value]; |
114 | tree[item.generic_params].clone() | 114 | tree[item.generic_params].clone() |
115 | } | 115 | } |
116 | GenericDefId::AdtId(AdtId::UnionId(id)) => { | 116 | GenericDefId::AdtId(AdtId::UnionId(id)) => { |
117 | let id = id.lookup(db).id; | 117 | let id = id.lookup(db).id; |
118 | let tree = db.item_tree(id.file_id); | 118 | let tree = id.item_tree(db); |
119 | let item = &tree[id.value]; | 119 | let item = &tree[id.value]; |
120 | tree[item.generic_params].clone() | 120 | tree[item.generic_params].clone() |
121 | } | 121 | } |
122 | GenericDefId::TraitId(id) => { | 122 | GenericDefId::TraitId(id) => { |
123 | let id = id.lookup(db).id; | 123 | let id = id.lookup(db).id; |
124 | let tree = db.item_tree(id.file_id); | 124 | let tree = id.item_tree(db); |
125 | let item = &tree[id.value]; | 125 | let item = &tree[id.value]; |
126 | tree[item.generic_params].clone() | 126 | tree[item.generic_params].clone() |
127 | } | 127 | } |
128 | GenericDefId::TypeAliasId(id) => { | 128 | GenericDefId::TypeAliasId(id) => { |
129 | let id = id.lookup(db).id; | 129 | let id = id.lookup(db).id; |
130 | let tree = db.item_tree(id.file_id); | 130 | let tree = id.item_tree(db); |
131 | let item = &tree[id.value]; | 131 | let item = &tree[id.value]; |
132 | tree[item.generic_params].clone() | 132 | tree[item.generic_params].clone() |
133 | } | 133 | } |
134 | GenericDefId::ImplId(id) => { | 134 | GenericDefId::ImplId(id) => { |
135 | let id = id.lookup(db).id; | 135 | let id = id.lookup(db).id; |
136 | let tree = db.item_tree(id.file_id); | 136 | let tree = id.item_tree(db); |
137 | let item = &tree[id.value]; | 137 | let item = &tree[id.value]; |
138 | tree[item.generic_params].clone() | 138 | tree[item.generic_params].clone() |
139 | } | 139 | } |
diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs index 4477bdd36..ae2475b4e 100644 --- a/crates/hir_def/src/item_tree.rs +++ b/crates/hir_def/src/item_tree.rs | |||
@@ -76,7 +76,7 @@ pub struct ItemTree { | |||
76 | } | 76 | } |
77 | 77 | ||
78 | impl ItemTree { | 78 | impl ItemTree { |
79 | pub(crate) fn item_tree_query(db: &dyn DefDatabase, file_id: HirFileId) -> Arc<ItemTree> { | 79 | pub(crate) fn file_item_tree_query(db: &dyn DefDatabase, file_id: HirFileId) -> Arc<ItemTree> { |
80 | let _p = profile::span("item_tree_query").detail(|| format!("{:?}", file_id)); | 80 | let _p = profile::span("item_tree_query").detail(|| format!("{:?}", file_id)); |
81 | let syntax = if let Some(node) = db.parse_or_expand(file_id) { | 81 | let syntax = if let Some(node) = db.parse_or_expand(file_id) { |
82 | if node.kind() == SyntaxKind::ERROR { | 82 | if node.kind() == SyntaxKind::ERROR { |
@@ -138,6 +138,7 @@ impl ItemTree { | |||
138 | imports, | 138 | imports, |
139 | extern_crates, | 139 | extern_crates, |
140 | functions, | 140 | functions, |
141 | params, | ||
141 | structs, | 142 | structs, |
142 | fields, | 143 | fields, |
143 | unions, | 144 | unions, |
@@ -161,6 +162,7 @@ impl ItemTree { | |||
161 | imports.shrink_to_fit(); | 162 | imports.shrink_to_fit(); |
162 | extern_crates.shrink_to_fit(); | 163 | extern_crates.shrink_to_fit(); |
163 | functions.shrink_to_fit(); | 164 | functions.shrink_to_fit(); |
165 | params.shrink_to_fit(); | ||
164 | structs.shrink_to_fit(); | 166 | structs.shrink_to_fit(); |
165 | fields.shrink_to_fit(); | 167 | fields.shrink_to_fit(); |
166 | unions.shrink_to_fit(); | 168 | unions.shrink_to_fit(); |
@@ -307,6 +309,7 @@ struct ItemTreeData { | |||
307 | imports: Arena<Import>, | 309 | imports: Arena<Import>, |
308 | extern_crates: Arena<ExternCrate>, | 310 | extern_crates: Arena<ExternCrate>, |
309 | functions: Arena<Function>, | 311 | functions: Arena<Function>, |
312 | params: Arena<Param>, | ||
310 | structs: Arena<Struct>, | 313 | structs: Arena<Struct>, |
311 | fields: Arena<Field>, | 314 | fields: Arena<Field>, |
312 | unions: Arena<Union>, | 315 | unions: Arena<Union>, |
@@ -338,6 +341,7 @@ pub enum AttrOwner { | |||
338 | 341 | ||
339 | Variant(Idx<Variant>), | 342 | Variant(Idx<Variant>), |
340 | Field(Idx<Field>), | 343 | Field(Idx<Field>), |
344 | Param(Idx<Param>), | ||
341 | } | 345 | } |
342 | 346 | ||
343 | macro_rules! from_attrs { | 347 | macro_rules! from_attrs { |
@@ -352,7 +356,7 @@ macro_rules! from_attrs { | |||
352 | }; | 356 | }; |
353 | } | 357 | } |
354 | 358 | ||
355 | from_attrs!(ModItem(ModItem), Variant(Idx<Variant>), Field(Idx<Field>)); | 359 | from_attrs!(ModItem(ModItem), Variant(Idx<Variant>), Field(Idx<Field>), Param(Idx<Param>)); |
356 | 360 | ||
357 | /// Trait implemented by all item nodes in the item tree. | 361 | /// Trait implemented by all item nodes in the item tree. |
358 | pub trait ItemTreeNode: Clone { | 362 | pub trait ItemTreeNode: Clone { |
@@ -401,7 +405,47 @@ impl<N: ItemTreeNode> fmt::Debug for FileItemTreeId<N> { | |||
401 | } | 405 | } |
402 | } | 406 | } |
403 | 407 | ||
404 | pub type ItemTreeId<N> = InFile<FileItemTreeId<N>>; | 408 | #[derive(Debug)] |
409 | pub struct ItemTreeId<N: ItemTreeNode> { | ||
410 | file: HirFileId, | ||
411 | pub value: FileItemTreeId<N>, | ||
412 | } | ||
413 | |||
414 | impl<N: ItemTreeNode> ItemTreeId<N> { | ||
415 | pub fn new(file: HirFileId, idx: FileItemTreeId<N>) -> Self { | ||
416 | Self { file, value: idx } | ||
417 | } | ||
418 | |||
419 | pub fn file_id(self) -> HirFileId { | ||
420 | self.file | ||
421 | } | ||
422 | |||
423 | pub fn item_tree(self, db: &dyn DefDatabase) -> Arc<ItemTree> { | ||
424 | db.file_item_tree(self.file) | ||
425 | } | ||
426 | } | ||
427 | |||
428 | impl<N: ItemTreeNode> Copy for ItemTreeId<N> {} | ||
429 | impl<N: ItemTreeNode> Clone for ItemTreeId<N> { | ||
430 | fn clone(&self) -> Self { | ||
431 | *self | ||
432 | } | ||
433 | } | ||
434 | |||
435 | impl<N: ItemTreeNode> PartialEq for ItemTreeId<N> { | ||
436 | fn eq(&self, other: &Self) -> bool { | ||
437 | self.file == other.file && self.value == other.value | ||
438 | } | ||
439 | } | ||
440 | |||
441 | impl<N: ItemTreeNode> Eq for ItemTreeId<N> {} | ||
442 | |||
443 | impl<N: ItemTreeNode> Hash for ItemTreeId<N> { | ||
444 | fn hash<H: Hasher>(&self, state: &mut H) { | ||
445 | self.file.hash(state); | ||
446 | self.value.hash(state); | ||
447 | } | ||
448 | } | ||
405 | 449 | ||
406 | macro_rules! mod_items { | 450 | macro_rules! mod_items { |
407 | ( $( $typ:ident in $fld:ident -> $ast:ty ),+ $(,)? ) => { | 451 | ( $( $typ:ident in $fld:ident -> $ast:ty ),+ $(,)? ) => { |
@@ -488,7 +532,7 @@ macro_rules! impl_index { | |||
488 | }; | 532 | }; |
489 | } | 533 | } |
490 | 534 | ||
491 | impl_index!(fields: Field, variants: Variant); | 535 | impl_index!(fields: Field, variants: Variant, params: Param); |
492 | 536 | ||
493 | impl Index<RawVisibilityId> for ItemTree { | 537 | impl Index<RawVisibilityId> for ItemTree { |
494 | type Output = RawVisibility; | 538 | type Output = RawVisibility; |
@@ -564,12 +608,17 @@ pub struct Function { | |||
564 | /// Whether the function is located in an `extern` block (*not* whether it is an | 608 | /// Whether the function is located in an `extern` block (*not* whether it is an |
565 | /// `extern "abi" fn`). | 609 | /// `extern "abi" fn`). |
566 | pub is_in_extern_block: bool, | 610 | pub is_in_extern_block: bool, |
567 | pub params: Box<[Idx<TypeRef>]>, | 611 | pub params: IdRange<Param>, |
568 | pub is_varargs: bool, | ||
569 | pub ret_type: Idx<TypeRef>, | 612 | pub ret_type: Idx<TypeRef>, |
570 | pub ast_id: FileAstId<ast::Fn>, | 613 | pub ast_id: FileAstId<ast::Fn>, |
571 | } | 614 | } |
572 | 615 | ||
616 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
617 | pub enum Param { | ||
618 | Normal(Idx<TypeRef>), | ||
619 | Varargs, | ||
620 | } | ||
621 | |||
573 | #[derive(Debug, Clone, PartialEq, Eq)] | 622 | #[derive(Debug, Clone, PartialEq, Eq)] |
574 | pub struct FunctionQualifier { | 623 | pub struct FunctionQualifier { |
575 | pub is_default: bool, | 624 | pub is_default: bool, |
@@ -800,6 +849,7 @@ pub struct Variant { | |||
800 | pub fields: Fields, | 849 | pub fields: Fields, |
801 | } | 850 | } |
802 | 851 | ||
852 | /// A range of densely allocated ItemTree IDs. | ||
803 | pub struct IdRange<T> { | 853 | pub struct IdRange<T> { |
804 | range: Range<u32>, | 854 | range: Range<u32>, |
805 | _p: PhantomData<T>, | 855 | _p: PhantomData<T>, |
@@ -818,6 +868,12 @@ impl<T> Iterator for IdRange<T> { | |||
818 | } | 868 | } |
819 | } | 869 | } |
820 | 870 | ||
871 | impl<T> DoubleEndedIterator for IdRange<T> { | ||
872 | fn next_back(&mut self) -> Option<Self::Item> { | ||
873 | self.range.next_back().map(|raw| Idx::from_raw(raw.into())) | ||
874 | } | ||
875 | } | ||
876 | |||
821 | impl<T> fmt::Debug for IdRange<T> { | 877 | impl<T> fmt::Debug for IdRange<T> { |
822 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | 878 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
823 | f.debug_tuple(&format!("IdRange::<{}>", type_name::<T>())).field(&self.range).finish() | 879 | f.debug_tuple(&format!("IdRange::<{}>", type_name::<T>())).field(&self.range).finish() |
diff --git a/crates/hir_def/src/item_tree/lower.rs b/crates/hir_def/src/item_tree/lower.rs index d684b89d0..d3fe1ce1e 100644 --- a/crates/hir_def/src/item_tree/lower.rs +++ b/crates/hir_def/src/item_tree/lower.rs | |||
@@ -341,8 +341,8 @@ impl Ctx { | |||
341 | let visibility = self.lower_visibility(func); | 341 | let visibility = self.lower_visibility(func); |
342 | let name = func.name()?.as_name(); | 342 | let name = func.name()?.as_name(); |
343 | 343 | ||
344 | let mut params = Vec::new(); | ||
345 | let mut has_self_param = false; | 344 | let mut has_self_param = false; |
345 | let start_param = self.next_param_idx(); | ||
346 | if let Some(param_list) = func.param_list() { | 346 | if let Some(param_list) = func.param_list() { |
347 | if let Some(self_param) = param_list.self_param() { | 347 | if let Some(self_param) = param_list.self_param() { |
348 | let self_type = match self_param.ty() { | 348 | let self_type = match self_param.ty() { |
@@ -364,22 +364,25 @@ impl Ctx { | |||
364 | } | 364 | } |
365 | } | 365 | } |
366 | }; | 366 | }; |
367 | params.push(self_type); | 367 | let ty = self.data().type_refs.intern(self_type); |
368 | let idx = self.data().params.alloc(Param::Normal(ty)); | ||
369 | self.add_attrs(idx.into(), RawAttrs::new(&self_param, &self.hygiene)); | ||
368 | has_self_param = true; | 370 | has_self_param = true; |
369 | } | 371 | } |
370 | for param in param_list.params() { | 372 | for param in param_list.params() { |
371 | let type_ref = TypeRef::from_ast_opt(&self.body_ctx, param.ty()); | 373 | let idx = match param.dotdotdot_token() { |
372 | params.push(type_ref); | 374 | Some(_) => self.data().params.alloc(Param::Varargs), |
373 | } | 375 | None => { |
374 | } | 376 | let type_ref = TypeRef::from_ast_opt(&self.body_ctx, param.ty()); |
375 | let params = params.into_iter().map(|param| self.data().type_refs.intern(param)).collect(); | 377 | let ty = self.data().type_refs.intern(type_ref); |
376 | 378 | self.data().params.alloc(Param::Normal(ty)) | |
377 | let mut is_varargs = false; | 379 | } |
378 | if let Some(params) = func.param_list() { | 380 | }; |
379 | if let Some(last) = params.params().last() { | 381 | self.add_attrs(idx.into(), RawAttrs::new(¶m, &self.hygiene)); |
380 | is_varargs = last.dotdotdot_token().is_some(); | ||
381 | } | 382 | } |
382 | } | 383 | } |
384 | let end_param = self.next_param_idx(); | ||
385 | let params = IdRange::new(start_param..end_param); | ||
383 | 386 | ||
384 | let ret_type = match func.ret_type().and_then(|rt| rt.ty()) { | 387 | let ret_type = match func.ret_type().and_then(|rt| rt.ty()) { |
385 | Some(type_ref) => TypeRef::from_ast(&self.body_ctx, type_ref), | 388 | Some(type_ref) => TypeRef::from_ast(&self.body_ctx, type_ref), |
@@ -427,7 +430,6 @@ impl Ctx { | |||
427 | qualifier, | 430 | qualifier, |
428 | is_in_extern_block: false, | 431 | is_in_extern_block: false, |
429 | params, | 432 | params, |
430 | is_varargs, | ||
431 | ret_type, | 433 | ret_type, |
432 | ast_id, | 434 | ast_id, |
433 | }; | 435 | }; |
@@ -690,9 +692,11 @@ impl Ctx { | |||
690 | GenericsOwner::Function(func) => { | 692 | GenericsOwner::Function(func) => { |
691 | generics.fill(&self.body_ctx, sm, node); | 693 | generics.fill(&self.body_ctx, sm, node); |
692 | // lower `impl Trait` in arguments | 694 | // lower `impl Trait` in arguments |
693 | for param in &*func.params { | 695 | for id in func.params.clone() { |
694 | let param = self.data().type_refs.lookup(*param); | 696 | if let Param::Normal(ty) = self.data().params[id] { |
695 | generics.fill_implicit_impl_trait_args(param); | 697 | let ty = self.data().type_refs.lookup(ty); |
698 | generics.fill_implicit_impl_trait_args(ty); | ||
699 | } | ||
696 | } | 700 | } |
697 | } | 701 | } |
698 | GenericsOwner::Struct | 702 | GenericsOwner::Struct |
@@ -777,6 +781,11 @@ impl Ctx { | |||
777 | self.tree.data.as_ref().map_or(0, |data| data.variants.len() as u32), | 781 | self.tree.data.as_ref().map_or(0, |data| data.variants.len() as u32), |
778 | )) | 782 | )) |
779 | } | 783 | } |
784 | fn next_param_idx(&self) -> Idx<Param> { | ||
785 | Idx::from_raw(RawIdx::from( | ||
786 | self.tree.data.as_ref().map_or(0, |data| data.params.len() as u32), | ||
787 | )) | ||
788 | } | ||
780 | } | 789 | } |
781 | 790 | ||
782 | fn desugar_future_path(orig: TypeRef) -> Path { | 791 | fn desugar_future_path(orig: TypeRef) -> Path { |
diff --git a/crates/hir_def/src/nameres.rs b/crates/hir_def/src/nameres.rs index 003d668ca..c97be584e 100644 --- a/crates/hir_def/src/nameres.rs +++ b/crates/hir_def/src/nameres.rs | |||
@@ -213,7 +213,7 @@ impl DefMap { | |||
213 | ) -> Option<Arc<DefMap>> { | 213 | ) -> Option<Arc<DefMap>> { |
214 | let block: BlockLoc = db.lookup_intern_block(block_id); | 214 | let block: BlockLoc = db.lookup_intern_block(block_id); |
215 | 215 | ||
216 | let item_tree = db.item_tree(block.ast_id.file_id); | 216 | let item_tree = db.file_item_tree(block.ast_id.file_id); |
217 | if item_tree.inner_items_of_block(block.ast_id.value).is_empty() { | 217 | if item_tree.inner_items_of_block(block.ast_id.value).is_empty() { |
218 | return None; | 218 | return None; |
219 | } | 219 | } |
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs index 81cf652b0..d0fefb5af 100644 --- a/crates/hir_def/src/nameres/collector.rs +++ b/crates/hir_def/src/nameres/collector.rs | |||
@@ -242,7 +242,7 @@ struct DefCollector<'a> { | |||
242 | impl DefCollector<'_> { | 242 | impl DefCollector<'_> { |
243 | fn seed_with_top_level(&mut self) { | 243 | fn seed_with_top_level(&mut self) { |
244 | let file_id = self.db.crate_graph()[self.def_map.krate].root_file_id; | 244 | let file_id = self.db.crate_graph()[self.def_map.krate].root_file_id; |
245 | let item_tree = self.db.item_tree(file_id.into()); | 245 | let item_tree = self.db.file_item_tree(file_id.into()); |
246 | let module_id = self.def_map.root; | 246 | let module_id = self.def_map.root; |
247 | self.def_map.modules[module_id].origin = ModuleOrigin::CrateRoot { definition: file_id }; | 247 | self.def_map.modules[module_id].origin = ModuleOrigin::CrateRoot { definition: file_id }; |
248 | if item_tree | 248 | if item_tree |
@@ -263,7 +263,7 @@ impl DefCollector<'_> { | |||
263 | } | 263 | } |
264 | 264 | ||
265 | fn seed_with_inner(&mut self, block: AstId<ast::BlockExpr>) { | 265 | fn seed_with_inner(&mut self, block: AstId<ast::BlockExpr>) { |
266 | let item_tree = self.db.item_tree(block.file_id); | 266 | let item_tree = self.db.file_item_tree(block.file_id); |
267 | let module_id = self.def_map.root; | 267 | let module_id = self.def_map.root; |
268 | self.def_map.modules[module_id].origin = ModuleOrigin::BlockExpr { block }; | 268 | self.def_map.modules[module_id].origin = ModuleOrigin::BlockExpr { block }; |
269 | if item_tree | 269 | if item_tree |
@@ -656,26 +656,28 @@ impl DefCollector<'_> { | |||
656 | } | 656 | } |
657 | } | 657 | } |
658 | } else { | 658 | } else { |
659 | match import.path.segments().last() { | 659 | let name = match &import.alias { |
660 | Some(last_segment) => { | 660 | Some(ImportAlias::Alias(name)) => Some(name.clone()), |
661 | let name = match &import.alias { | 661 | Some(ImportAlias::Underscore) => None, |
662 | Some(ImportAlias::Alias(name)) => Some(name.clone()), | 662 | None => match import.path.segments().last() { |
663 | Some(ImportAlias::Underscore) => None, | 663 | Some(last_segment) => Some(last_segment.clone()), |
664 | None => Some(last_segment.clone()), | 664 | None => { |
665 | }; | 665 | cov_mark::hit!(bogus_paths); |
666 | log::debug!("resolved import {:?} ({:?}) to {:?}", name, import, def); | 666 | return; |
667 | |||
668 | // extern crates in the crate root are special-cased to insert entries into the extern prelude: rust-lang/rust#54658 | ||
669 | if import.is_extern_crate && module_id == self.def_map.root { | ||
670 | if let (Some(def), Some(name)) = (def.take_types(), name.as_ref()) { | ||
671 | self.def_map.extern_prelude.insert(name.clone(), def); | ||
672 | } | ||
673 | } | 667 | } |
668 | }, | ||
669 | }; | ||
670 | |||
671 | log::debug!("resolved import {:?} ({:?}) to {:?}", name, import, def); | ||
674 | 672 | ||
675 | self.update(module_id, &[(name, def)], vis, ImportType::Named); | 673 | // extern crates in the crate root are special-cased to insert entries into the extern prelude: rust-lang/rust#54658 |
674 | if import.is_extern_crate && module_id == self.def_map.root { | ||
675 | if let (Some(def), Some(name)) = (def.take_types(), name.as_ref()) { | ||
676 | self.def_map.extern_prelude.insert(name.clone(), def); | ||
676 | } | 677 | } |
677 | None => cov_mark::hit!(bogus_paths), | ||
678 | } | 678 | } |
679 | |||
680 | self.update(module_id, &[(name, def)], vis, ImportType::Named); | ||
679 | } | 681 | } |
680 | } | 682 | } |
681 | 683 | ||
@@ -893,7 +895,7 @@ impl DefCollector<'_> { | |||
893 | } | 895 | } |
894 | 896 | ||
895 | // Then, fetch and process the item tree. This will reuse the expansion result from above. | 897 | // Then, fetch and process the item tree. This will reuse the expansion result from above. |
896 | let item_tree = self.db.item_tree(file_id); | 898 | let item_tree = self.db.file_item_tree(file_id); |
897 | let mod_dir = self.mod_dirs[&module_id].clone(); | 899 | let mod_dir = self.mod_dirs[&module_id].clone(); |
898 | ModCollector { | 900 | ModCollector { |
899 | def_collector: &mut *self, | 901 | def_collector: &mut *self, |
@@ -949,21 +951,21 @@ impl DefCollector<'_> { | |||
949 | let mut diagnosed_extern_crates = FxHashSet::default(); | 951 | let mut diagnosed_extern_crates = FxHashSet::default(); |
950 | for directive in &self.unresolved_imports { | 952 | for directive in &self.unresolved_imports { |
951 | if let ImportSource::ExternCrate(krate) = directive.import.source { | 953 | if let ImportSource::ExternCrate(krate) = directive.import.source { |
952 | let item_tree = self.db.item_tree(krate.file_id); | 954 | let item_tree = krate.item_tree(self.db); |
953 | let extern_crate = &item_tree[krate.value]; | 955 | let extern_crate = &item_tree[krate.value]; |
954 | 956 | ||
955 | diagnosed_extern_crates.insert(extern_crate.name.clone()); | 957 | diagnosed_extern_crates.insert(extern_crate.name.clone()); |
956 | 958 | ||
957 | self.def_map.diagnostics.push(DefDiagnostic::unresolved_extern_crate( | 959 | self.def_map.diagnostics.push(DefDiagnostic::unresolved_extern_crate( |
958 | directive.module_id, | 960 | directive.module_id, |
959 | InFile::new(krate.file_id, extern_crate.ast_id), | 961 | InFile::new(krate.file_id(), extern_crate.ast_id), |
960 | )); | 962 | )); |
961 | } | 963 | } |
962 | } | 964 | } |
963 | 965 | ||
964 | for directive in &self.unresolved_imports { | 966 | for directive in &self.unresolved_imports { |
965 | if let ImportSource::Import(import) = &directive.import.source { | 967 | if let ImportSource::Import(import) = &directive.import.source { |
966 | let item_tree = self.db.item_tree(import.file_id); | 968 | let item_tree = import.item_tree(self.db); |
967 | let import_data = &item_tree[import.value]; | 969 | let import_data = &item_tree[import.value]; |
968 | 970 | ||
969 | match (import_data.path.segments().first(), &import_data.path.kind) { | 971 | match (import_data.path.segments().first(), &import_data.path.kind) { |
@@ -977,7 +979,7 @@ impl DefCollector<'_> { | |||
977 | 979 | ||
978 | self.def_map.diagnostics.push(DefDiagnostic::unresolved_import( | 980 | self.def_map.diagnostics.push(DefDiagnostic::unresolved_import( |
979 | directive.module_id, | 981 | directive.module_id, |
980 | InFile::new(import.file_id, import_data.ast_id), | 982 | InFile::new(import.file_id(), import_data.ast_id), |
981 | import_data.index, | 983 | import_data.index, |
982 | )); | 984 | )); |
983 | } | 985 | } |
@@ -1053,7 +1055,7 @@ impl ModCollector<'_, '_> { | |||
1053 | self.def_collector.db, | 1055 | self.def_collector.db, |
1054 | krate, | 1056 | krate, |
1055 | &self.item_tree, | 1057 | &self.item_tree, |
1056 | InFile::new(self.file_id, import_id), | 1058 | ItemTreeId::new(self.file_id, import_id), |
1057 | ), | 1059 | ), |
1058 | status: PartialResolvedImport::Unresolved, | 1060 | status: PartialResolvedImport::Unresolved, |
1059 | }) | 1061 | }) |
@@ -1065,7 +1067,7 @@ impl ModCollector<'_, '_> { | |||
1065 | self.def_collector.db, | 1067 | self.def_collector.db, |
1066 | krate, | 1068 | krate, |
1067 | &self.item_tree, | 1069 | &self.item_tree, |
1068 | InFile::new(self.file_id, import_id), | 1070 | ItemTreeId::new(self.file_id, import_id), |
1069 | ), | 1071 | ), |
1070 | status: PartialResolvedImport::Unresolved, | 1072 | status: PartialResolvedImport::Unresolved, |
1071 | }) | 1073 | }) |
@@ -1297,7 +1299,7 @@ impl ModCollector<'_, '_> { | |||
1297 | Some((file_id, is_mod_rs)), | 1299 | Some((file_id, is_mod_rs)), |
1298 | &self.item_tree[module.visibility], | 1300 | &self.item_tree[module.visibility], |
1299 | ); | 1301 | ); |
1300 | let item_tree = db.item_tree(file_id.into()); | 1302 | let item_tree = db.file_item_tree(file_id.into()); |
1301 | ModCollector { | 1303 | ModCollector { |
1302 | def_collector: &mut *self.def_collector, | 1304 | def_collector: &mut *self.def_collector, |
1303 | macro_depth: self.macro_depth, | 1305 | macro_depth: self.macro_depth, |
@@ -1469,7 +1471,9 @@ impl ModCollector<'_, '_> { | |||
1469 | ) | 1471 | ) |
1470 | }) | 1472 | }) |
1471 | }, | 1473 | }, |
1472 | &mut |err| error = Some(err), | 1474 | &mut |err| { |
1475 | error.get_or_insert(err); | ||
1476 | }, | ||
1473 | ) { | 1477 | ) { |
1474 | Ok(Ok(macro_call_id)) => { | 1478 | Ok(Ok(macro_call_id)) => { |
1475 | self.def_collector.unexpanded_macros.push(MacroDirective { | 1479 | self.def_collector.unexpanded_macros.push(MacroDirective { |
diff --git a/crates/hir_def/src/nameres/tests.rs b/crates/hir_def/src/nameres/tests.rs index de3aa4f9a..4f2e7a2f9 100644 --- a/crates/hir_def/src/nameres/tests.rs +++ b/crates/hir_def/src/nameres/tests.rs | |||
@@ -713,3 +713,22 @@ pub fn f() {} | |||
713 | "#]], | 713 | "#]], |
714 | ); | 714 | ); |
715 | } | 715 | } |
716 | |||
717 | #[test] | ||
718 | fn use_crate_as() { | ||
719 | check( | ||
720 | r#" | ||
721 | use crate as foo; | ||
722 | |||
723 | use foo::bar as baz; | ||
724 | |||
725 | fn bar() {} | ||
726 | "#, | ||
727 | expect![[r#" | ||
728 | crate | ||
729 | bar: v | ||
730 | baz: v | ||
731 | foo: t | ||
732 | "#]], | ||
733 | ); | ||
734 | } | ||
diff --git a/crates/hir_def/src/nameres/tests/diagnostics.rs b/crates/hir_def/src/nameres/tests/diagnostics.rs index c22ef46fd..a89061c2e 100644 --- a/crates/hir_def/src/nameres/tests/diagnostics.rs +++ b/crates/hir_def/src/nameres/tests/diagnostics.rs | |||
@@ -196,7 +196,24 @@ fn builtin_macro_fails_expansion() { | |||
196 | macro_rules! include { () => {} } | 196 | macro_rules! include { () => {} } |
197 | 197 | ||
198 | include!("doesntexist"); | 198 | include!("doesntexist"); |
199 | //^^^^^^^^^^^^^^^^^^^^^^^^ could not convert tokens | 199 | //^^^^^^^^^^^^^^^^^^^^^^^^ failed to load file `doesntexist` |
200 | "#, | ||
201 | ); | ||
202 | } | ||
203 | |||
204 | #[test] | ||
205 | fn good_out_dir_diagnostic() { | ||
206 | check_diagnostics( | ||
207 | r#" | ||
208 | #[rustc_builtin_macro] | ||
209 | macro_rules! include { () => {} } | ||
210 | #[rustc_builtin_macro] | ||
211 | macro_rules! env { () => {} } | ||
212 | #[rustc_builtin_macro] | ||
213 | macro_rules! concat { () => {} } | ||
214 | |||
215 | include!(concat!(env!("OUT_DIR"), "/out.rs")); | ||
216 | //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `OUT_DIR` not set, enable "load out dirs from check" to fix | ||
200 | "#, | 217 | "#, |
201 | ); | 218 | ); |
202 | } | 219 | } |
diff --git a/crates/hir_def/src/src.rs b/crates/hir_def/src/src.rs index 751d4c052..24e57b469 100644 --- a/crates/hir_def/src/src.rs +++ b/crates/hir_def/src/src.rs | |||
@@ -14,12 +14,12 @@ impl<N: ItemTreeNode> HasSource for AssocItemLoc<N> { | |||
14 | type Value = N::Source; | 14 | type Value = N::Source; |
15 | 15 | ||
16 | fn source(&self, db: &dyn DefDatabase) -> InFile<N::Source> { | 16 | fn source(&self, db: &dyn DefDatabase) -> InFile<N::Source> { |
17 | let tree = db.item_tree(self.id.file_id); | 17 | let tree = self.id.item_tree(db); |
18 | let ast_id_map = db.ast_id_map(self.id.file_id); | 18 | let ast_id_map = db.ast_id_map(self.id.file_id()); |
19 | let root = db.parse_or_expand(self.id.file_id).unwrap(); | 19 | let root = db.parse_or_expand(self.id.file_id()).unwrap(); |
20 | let node = &tree[self.id.value]; | 20 | let node = &tree[self.id.value]; |
21 | 21 | ||
22 | InFile::new(self.id.file_id, ast_id_map.get(node.ast_id()).to_node(&root)) | 22 | InFile::new(self.id.file_id(), ast_id_map.get(node.ast_id()).to_node(&root)) |
23 | } | 23 | } |
24 | } | 24 | } |
25 | 25 | ||
@@ -27,12 +27,12 @@ impl<N: ItemTreeNode> HasSource for ItemLoc<N> { | |||
27 | type Value = N::Source; | 27 | type Value = N::Source; |
28 | 28 | ||
29 | fn source(&self, db: &dyn DefDatabase) -> InFile<N::Source> { | 29 | fn source(&self, db: &dyn DefDatabase) -> InFile<N::Source> { |
30 | let tree = db.item_tree(self.id.file_id); | 30 | let tree = self.id.item_tree(db); |
31 | let ast_id_map = db.ast_id_map(self.id.file_id); | 31 | let ast_id_map = db.ast_id_map(self.id.file_id()); |
32 | let root = db.parse_or_expand(self.id.file_id).unwrap(); | 32 | let root = db.parse_or_expand(self.id.file_id()).unwrap(); |
33 | let node = &tree[self.id.value]; | 33 | let node = &tree[self.id.value]; |
34 | 34 | ||
35 | InFile::new(self.id.file_id, ast_id_map.get(node.ast_id()).to_node(&root)) | 35 | InFile::new(self.id.file_id(), ast_id_map.get(node.ast_id()).to_node(&root)) |
36 | } | 36 | } |
37 | } | 37 | } |
38 | 38 | ||
diff --git a/crates/hir_expand/src/builtin_macro.rs b/crates/hir_expand/src/builtin_macro.rs index 2a79c892b..fce09a9e7 100644 --- a/crates/hir_expand/src/builtin_macro.rs +++ b/crates/hir_expand/src/builtin_macro.rs | |||
@@ -333,17 +333,19 @@ fn concat_expand( | |||
333 | fn relative_file( | 333 | fn relative_file( |
334 | db: &dyn AstDatabase, | 334 | db: &dyn AstDatabase, |
335 | call_id: MacroCallId, | 335 | call_id: MacroCallId, |
336 | path: &str, | 336 | path_str: &str, |
337 | allow_recursion: bool, | 337 | allow_recursion: bool, |
338 | ) -> Option<FileId> { | 338 | ) -> Result<FileId, mbe::ExpandError> { |
339 | let call_site = call_id.as_file().original_file(db); | 339 | let call_site = call_id.as_file().original_file(db); |
340 | let path = AnchoredPath { anchor: call_site, path }; | 340 | let path = AnchoredPath { anchor: call_site, path: path_str }; |
341 | let res = db.resolve_path(path)?; | 341 | let res = db |
342 | .resolve_path(path) | ||
343 | .ok_or_else(|| mbe::ExpandError::Other(format!("failed to load file `{}`", path_str)))?; | ||
342 | // Prevent include itself | 344 | // Prevent include itself |
343 | if res == call_site && !allow_recursion { | 345 | if res == call_site && !allow_recursion { |
344 | None | 346 | Err(mbe::ExpandError::Other(format!("recursive inclusion of `{}`", path_str))) |
345 | } else { | 347 | } else { |
346 | Some(res) | 348 | Ok(res) |
347 | } | 349 | } |
348 | } | 350 | } |
349 | 351 | ||
@@ -364,8 +366,7 @@ fn include_expand( | |||
364 | ) -> ExpandResult<Option<(tt::Subtree, FragmentKind)>> { | 366 | ) -> ExpandResult<Option<(tt::Subtree, FragmentKind)>> { |
365 | let res = (|| { | 367 | let res = (|| { |
366 | let path = parse_string(tt)?; | 368 | let path = parse_string(tt)?; |
367 | let file_id = relative_file(db, arg_id.into(), &path, false) | 369 | let file_id = relative_file(db, arg_id.into(), &path, false)?; |
368 | .ok_or_else(|| mbe::ExpandError::ConversionError)?; | ||
369 | 370 | ||
370 | Ok(parse_to_token_tree(&db.file_text(file_id)) | 371 | Ok(parse_to_token_tree(&db.file_text(file_id)) |
371 | .ok_or_else(|| mbe::ExpandError::ConversionError)? | 372 | .ok_or_else(|| mbe::ExpandError::ConversionError)? |
@@ -417,8 +418,8 @@ fn include_str_expand( | |||
417 | // Ideally, we'd be able to offer a precise expansion if the user asks for macro | 418 | // Ideally, we'd be able to offer a precise expansion if the user asks for macro |
418 | // expansion. | 419 | // expansion. |
419 | let file_id = match relative_file(db, arg_id.into(), &path, true) { | 420 | let file_id = match relative_file(db, arg_id.into(), &path, true) { |
420 | Some(file_id) => file_id, | 421 | Ok(file_id) => file_id, |
421 | None => { | 422 | Err(_) => { |
422 | return ExpandResult::ok(Some((quote!(""), FragmentKind::Expr))); | 423 | return ExpandResult::ok(Some((quote!(""), FragmentKind::Expr))); |
423 | } | 424 | } |
424 | }; | 425 | }; |
diff --git a/crates/hir_ty/src/diagnostics/expr.rs b/crates/hir_ty/src/diagnostics/expr.rs index 71b2cade0..3909ad354 100644 --- a/crates/hir_ty/src/diagnostics/expr.rs +++ b/crates/hir_ty/src/diagnostics/expr.rs | |||
@@ -690,4 +690,61 @@ fn main() { | |||
690 | "#, | 690 | "#, |
691 | ) | 691 | ) |
692 | } | 692 | } |
693 | |||
694 | #[test] | ||
695 | fn cfgd_out_call_arguments() { | ||
696 | check_diagnostics( | ||
697 | r#" | ||
698 | struct C(#[cfg(FALSE)] ()); | ||
699 | impl C { | ||
700 | fn new() -> Self { | ||
701 | Self( | ||
702 | #[cfg(FALSE)] | ||
703 | (), | ||
704 | ) | ||
705 | } | ||
706 | |||
707 | fn method(&self) {} | ||
708 | } | ||
709 | |||
710 | fn main() { | ||
711 | C::new().method(#[cfg(FALSE)] 0); | ||
712 | } | ||
713 | "#, | ||
714 | ); | ||
715 | } | ||
716 | |||
717 | #[test] | ||
718 | fn cfgd_out_fn_params() { | ||
719 | check_diagnostics( | ||
720 | r#" | ||
721 | fn foo(#[cfg(NEVER)] x: ()) {} | ||
722 | |||
723 | struct S; | ||
724 | |||
725 | impl S { | ||
726 | fn method(#[cfg(NEVER)] self) {} | ||
727 | fn method2(#[cfg(NEVER)] self, arg: u8) {} | ||
728 | fn method3(self, #[cfg(NEVER)] arg: u8) {} | ||
729 | } | ||
730 | |||
731 | extern "C" { | ||
732 | fn fixed(fixed: u8, #[cfg(NEVER)] ...); | ||
733 | fn varargs(#[cfg(not(NEVER))] ...); | ||
734 | } | ||
735 | |||
736 | fn main() { | ||
737 | foo(); | ||
738 | S::method(); | ||
739 | S::method2(0); | ||
740 | S::method3(S); | ||
741 | S.method3(); | ||
742 | unsafe { | ||
743 | fixed(0); | ||
744 | varargs(1, 2, 3); | ||
745 | } | ||
746 | } | ||
747 | "#, | ||
748 | ) | ||
749 | } | ||
693 | } | 750 | } |
diff --git a/crates/hir_ty/src/tests/simple.rs b/crates/hir_ty/src/tests/simple.rs index f5069eba5..bcc43ed70 100644 --- a/crates/hir_ty/src/tests/simple.rs +++ b/crates/hir_ty/src/tests/simple.rs | |||
@@ -2545,3 +2545,22 @@ fn test() { | |||
2545 | "#]], | 2545 | "#]], |
2546 | ) | 2546 | ) |
2547 | } | 2547 | } |
2548 | |||
2549 | #[test] | ||
2550 | fn cfgd_out_assoc_items() { | ||
2551 | check_types( | ||
2552 | r#" | ||
2553 | struct S; | ||
2554 | |||
2555 | impl S { | ||
2556 | #[cfg(FALSE)] | ||
2557 | const C: S = S; | ||
2558 | } | ||
2559 | |||
2560 | fn f() { | ||
2561 | S::C; | ||
2562 | //^^^^ {unknown} | ||
2563 | } | ||
2564 | "#, | ||
2565 | ) | ||
2566 | } | ||
diff --git a/crates/hir_ty/src/tests/traits.rs b/crates/hir_ty/src/tests/traits.rs index 93d3ad020..8270fa219 100644 --- a/crates/hir_ty/src/tests/traits.rs +++ b/crates/hir_ty/src/tests/traits.rs | |||
@@ -3253,3 +3253,24 @@ fn f() { | |||
3253 | "#, | 3253 | "#, |
3254 | ); | 3254 | ); |
3255 | } | 3255 | } |
3256 | |||
3257 | #[test] | ||
3258 | fn nested_inner_function_calling_self() { | ||
3259 | check_infer( | ||
3260 | r#" | ||
3261 | struct S; | ||
3262 | fn f() { | ||
3263 | fn inner() -> S { | ||
3264 | let s = inner(); | ||
3265 | } | ||
3266 | } | ||
3267 | "#, | ||
3268 | expect![[r#" | ||
3269 | 17..73 '{ ... } }': () | ||
3270 | 39..71 '{ ... }': () | ||
3271 | 53..54 's': S | ||
3272 | 57..62 'inner': fn inner() -> S | ||
3273 | 57..64 'inner()': S | ||
3274 | "#]], | ||
3275 | ) | ||
3276 | } | ||
diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs index 5ea9fc4fb..c7c1f4fee 100644 --- a/crates/ide/src/doc_links.rs +++ b/crates/ide/src/doc_links.rs | |||
@@ -65,6 +65,8 @@ pub(crate) fn extract_definitions_from_markdown( | |||
65 | ) -> Vec<(String, Option<hir::Namespace>, Range<usize>)> { | 65 | ) -> Vec<(String, Option<hir::Namespace>, Range<usize>)> { |
66 | let mut res = vec![]; | 66 | let mut res = vec![]; |
67 | let mut cb = |link: BrokenLink| { | 67 | let mut cb = |link: BrokenLink| { |
68 | // These allocations are actually unnecessary but the lifetimes on BrokenLinkCallback are wrong | ||
69 | // this is fixed in the repo but not on the crates.io release yet | ||
68 | Some(( | 70 | Some(( |
69 | /*url*/ link.reference.to_owned().into(), | 71 | /*url*/ link.reference.to_owned().into(), |
70 | /*title*/ link.reference.to_owned().into(), | 72 | /*title*/ link.reference.to_owned().into(), |
@@ -72,13 +74,10 @@ pub(crate) fn extract_definitions_from_markdown( | |||
72 | }; | 74 | }; |
73 | let doc = Parser::new_with_broken_link_callback(markdown, Options::empty(), Some(&mut cb)); | 75 | let doc = Parser::new_with_broken_link_callback(markdown, Options::empty(), Some(&mut cb)); |
74 | for (event, range) in doc.into_offset_iter() { | 76 | for (event, range) in doc.into_offset_iter() { |
75 | match event { | 77 | if let Event::Start(Tag::Link(_, target, title)) = event { |
76 | Event::Start(Tag::Link(_link_type, ref target, ref title)) => { | 78 | let link = if target.is_empty() { title } else { target }; |
77 | let link = if target.is_empty() { title } else { target }; | 79 | let (link, ns) = parse_link(&link); |
78 | let (link, ns) = parse_link(link); | 80 | res.push((link.to_string(), ns, range)); |
79 | res.push((link.to_string(), ns, range)); | ||
80 | } | ||
81 | _ => {} | ||
82 | } | 81 | } |
83 | } | 82 | } |
84 | res | 83 | res |
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs index b71f4917c..598b47e41 100644 --- a/crates/ide/src/goto_definition.rs +++ b/crates/ide/src/goto_definition.rs | |||
@@ -1,3 +1,5 @@ | |||
1 | use std::ops::Range; | ||
2 | |||
1 | use either::Either; | 3 | use either::Either; |
2 | use hir::{HasAttrs, ModuleDef, Semantics}; | 4 | use hir::{HasAttrs, ModuleDef, Semantics}; |
3 | use ide_db::{ | 5 | use ide_db::{ |
@@ -5,7 +7,8 @@ use ide_db::{ | |||
5 | RootDatabase, | 7 | RootDatabase, |
6 | }; | 8 | }; |
7 | use syntax::{ | 9 | use syntax::{ |
8 | ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, TextSize, TokenAtOffset, T, | 10 | ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, TextRange, TextSize, |
11 | TokenAtOffset, T, | ||
9 | }; | 12 | }; |
10 | 13 | ||
11 | use crate::{ | 14 | use crate::{ |
@@ -92,17 +95,18 @@ fn extract_positioned_link_from_comment( | |||
92 | position: FilePosition, | 95 | position: FilePosition, |
93 | comment: &ast::Comment, | 96 | comment: &ast::Comment, |
94 | ) -> Option<(String, Option<hir::Namespace>)> { | 97 | ) -> Option<(String, Option<hir::Namespace>)> { |
95 | let comment_range = comment.syntax().text_range(); | ||
96 | let doc_comment = comment.doc_comment()?; | 98 | let doc_comment = comment.doc_comment()?; |
99 | let comment_start = | ||
100 | comment.syntax().text_range().start() + TextSize::from(comment.prefix().len() as u32); | ||
97 | let def_links = extract_definitions_from_markdown(doc_comment); | 101 | let def_links = extract_definitions_from_markdown(doc_comment); |
98 | let (def_link, ns, _) = def_links.iter().min_by_key(|(_, _, def_link_range)| { | 102 | let (def_link, ns, _) = def_links.into_iter().find(|&(_, _, Range { start, end })| { |
99 | let matched_position = comment_range.start() + TextSize::from(def_link_range.start as u32); | 103 | TextRange::at( |
100 | match position.offset.checked_sub(matched_position) { | 104 | comment_start + TextSize::from(start as u32), |
101 | Some(distance) => distance, | 105 | TextSize::from((end - start) as u32), |
102 | None => comment_range.end(), | 106 | ) |
103 | } | 107 | .contains(position.offset) |
104 | })?; | 108 | })?; |
105 | Some((def_link.to_string(), *ns)) | 109 | Some((def_link, ns)) |
106 | } | 110 | } |
107 | 111 | ||
108 | fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> { | 112 | fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> { |
@@ -1136,7 +1140,7 @@ fn foo<'foo>(_: &'foo ()) { | |||
1136 | fn goto_def_for_intra_doc_link_same_file() { | 1140 | fn goto_def_for_intra_doc_link_same_file() { |
1137 | check( | 1141 | check( |
1138 | r#" | 1142 | r#" |
1139 | /// Blah, [`bar`](bar) .. [`foo`](foo)$0 has [`bar`](bar) | 1143 | /// Blah, [`bar`](bar) .. [`foo`](foo$0) has [`bar`](bar) |
1140 | pub fn bar() { } | 1144 | pub fn bar() { } |
1141 | 1145 | ||
1142 | /// You might want to see [`std::fs::read()`] too. | 1146 | /// You might want to see [`std::fs::read()`] too. |
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index 325014622..15d309d7d 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs | |||
@@ -1533,12 +1533,21 @@ fn my() {} | |||
1533 | fn test_hover_struct_doc_comment() { | 1533 | fn test_hover_struct_doc_comment() { |
1534 | check( | 1534 | check( |
1535 | r#" | 1535 | r#" |
1536 | /// bar docs | 1536 | /// This is an example |
1537 | /// multiline doc | ||
1538 | /// | ||
1539 | /// # Example | ||
1540 | /// | ||
1541 | /// ``` | ||
1542 | /// let five = 5; | ||
1543 | /// | ||
1544 | /// assert_eq!(6, my_crate::add_one(5)); | ||
1545 | /// ``` | ||
1537 | struct Bar; | 1546 | struct Bar; |
1538 | 1547 | ||
1539 | fn foo() { let bar = Ba$0r; } | 1548 | fn foo() { let bar = Ba$0r; } |
1540 | "#, | 1549 | "#, |
1541 | expect![[r#" | 1550 | expect![[r##" |
1542 | *Bar* | 1551 | *Bar* |
1543 | 1552 | ||
1544 | ```rust | 1553 | ```rust |
@@ -1551,8 +1560,17 @@ fn foo() { let bar = Ba$0r; } | |||
1551 | 1560 | ||
1552 | --- | 1561 | --- |
1553 | 1562 | ||
1554 | bar docs | 1563 | This is an example |
1555 | "#]], | 1564 | multiline doc |
1565 | |||
1566 | # Example | ||
1567 | |||
1568 | ``` | ||
1569 | let five = 5; | ||
1570 | |||
1571 | assert_eq!(6, my_crate::add_one(5)); | ||
1572 | ``` | ||
1573 | "##]], | ||
1556 | ); | 1574 | ); |
1557 | } | 1575 | } |
1558 | 1576 | ||
@@ -3424,6 +3442,40 @@ mod Foo$0 { | |||
3424 | } | 3442 | } |
3425 | 3443 | ||
3426 | #[test] | 3444 | #[test] |
3445 | fn hover_doc_block_style_indentend() { | ||
3446 | check( | ||
3447 | r#" | ||
3448 | /** | ||
3449 | foo | ||
3450 | ```rust | ||
3451 | let x = 3; | ||
3452 | ``` | ||
3453 | */ | ||
3454 | fn foo$0() {} | ||
3455 | "#, | ||
3456 | expect![[r#" | ||
3457 | *foo* | ||
3458 | |||
3459 | ```rust | ||
3460 | test | ||
3461 | ``` | ||
3462 | |||
3463 | ```rust | ||
3464 | fn foo() | ||
3465 | ``` | ||
3466 | |||
3467 | --- | ||
3468 | |||
3469 | foo | ||
3470 | |||
3471 | ```rust | ||
3472 | let x = 3; | ||
3473 | ``` | ||
3474 | "#]], | ||
3475 | ); | ||
3476 | } | ||
3477 | |||
3478 | #[test] | ||
3427 | fn hover_comments_dont_highlight_parent() { | 3479 | fn hover_comments_dont_highlight_parent() { |
3428 | check_hover_no_result( | 3480 | check_hover_no_result( |
3429 | r#" | 3481 | r#" |
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs index 397e2126b..bea020b06 100644 --- a/crates/ide/src/runnables.rs +++ b/crates/ide/src/runnables.rs | |||
@@ -576,6 +576,20 @@ fn should_have_runnable_1() {} | |||
576 | /// ``` | 576 | /// ``` |
577 | fn should_have_runnable_2() {} | 577 | fn should_have_runnable_2() {} |
578 | 578 | ||
579 | /** | ||
580 | ```rust | ||
581 | let z = 55; | ||
582 | ``` | ||
583 | */ | ||
584 | fn should_have_no_runnable_3() {} | ||
585 | |||
586 | /** | ||
587 | ```rust | ||
588 | let z = 55; | ||
589 | ``` | ||
590 | */ | ||
591 | fn should_have_no_runnable_4() {} | ||
592 | |||
579 | /// ```no_run | 593 | /// ```no_run |
580 | /// let z = 55; | 594 | /// let z = 55; |
581 | /// ``` | 595 | /// ``` |
@@ -616,7 +630,7 @@ fn should_have_no_runnable_6() {} | |||
616 | struct StructWithRunnable(String); | 630 | struct StructWithRunnable(String); |
617 | 631 | ||
618 | "#, | 632 | "#, |
619 | &[&BIN, &DOCTEST, &DOCTEST, &DOCTEST, &DOCTEST], | 633 | &[&BIN, &DOCTEST, &DOCTEST, &DOCTEST, &DOCTEST, &DOCTEST, &DOCTEST], |
620 | expect![[r#" | 634 | expect![[r#" |
621 | [ | 635 | [ |
622 | Runnable { | 636 | Runnable { |
@@ -682,7 +696,37 @@ struct StructWithRunnable(String); | |||
682 | file_id: FileId( | 696 | file_id: FileId( |
683 | 0, | 697 | 0, |
684 | ), | 698 | ), |
685 | full_range: 756..821, | 699 | full_range: 256..320, |
700 | name: "should_have_no_runnable_3", | ||
701 | }, | ||
702 | kind: DocTest { | ||
703 | test_id: Path( | ||
704 | "should_have_no_runnable_3", | ||
705 | ), | ||
706 | }, | ||
707 | cfg: None, | ||
708 | }, | ||
709 | Runnable { | ||
710 | nav: NavigationTarget { | ||
711 | file_id: FileId( | ||
712 | 0, | ||
713 | ), | ||
714 | full_range: 322..398, | ||
715 | name: "should_have_no_runnable_4", | ||
716 | }, | ||
717 | kind: DocTest { | ||
718 | test_id: Path( | ||
719 | "should_have_no_runnable_4", | ||
720 | ), | ||
721 | }, | ||
722 | cfg: None, | ||
723 | }, | ||
724 | Runnable { | ||
725 | nav: NavigationTarget { | ||
726 | file_id: FileId( | ||
727 | 0, | ||
728 | ), | ||
729 | full_range: 900..965, | ||
686 | name: "StructWithRunnable", | 730 | name: "StructWithRunnable", |
687 | }, | 731 | }, |
688 | kind: DocTest { | 732 | kind: DocTest { |
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs index 870146d24..ba3447b3a 100644 --- a/crates/ide/src/syntax_highlighting.rs +++ b/crates/ide/src/syntax_highlighting.rs | |||
@@ -150,7 +150,7 @@ fn traverse( | |||
150 | WalkEvent::Enter(it) => it, | 150 | WalkEvent::Enter(it) => it, |
151 | WalkEvent::Leave(it) => { | 151 | WalkEvent::Leave(it) => { |
152 | if let Some(node) = it.as_node() { | 152 | if let Some(node) = it.as_node() { |
153 | inject::doc_comment(hl, node); | 153 | inject::doc_comment(hl, sema, node); |
154 | } | 154 | } |
155 | continue; | 155 | continue; |
156 | } | 156 | } |
diff --git a/crates/ide/src/syntax_highlighting/html.rs b/crates/ide/src/syntax_highlighting/html.rs index 0ee7bc96e..1d34731ab 100644 --- a/crates/ide/src/syntax_highlighting/html.rs +++ b/crates/ide/src/syntax_highlighting/html.rs | |||
@@ -59,6 +59,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
59 | .label { color: #DFAF8F; font-style: italic; } | 59 | .label { color: #DFAF8F; font-style: italic; } |
60 | .comment { color: #7F9F7F; } | 60 | .comment { color: #7F9F7F; } |
61 | .documentation { color: #629755; } | 61 | .documentation { color: #629755; } |
62 | .intra_doc_link { color: #A9C577; } | ||
62 | .injected { opacity: 0.65 ; } | 63 | .injected { opacity: 0.65 ; } |
63 | .struct, .enum { color: #7CB8BB; } | 64 | .struct, .enum { color: #7CB8BB; } |
64 | .enum_variant { color: #BDE0F3; } | 65 | .enum_variant { color: #BDE0F3; } |
diff --git a/crates/ide/src/syntax_highlighting/inject.rs b/crates/ide/src/syntax_highlighting/inject.rs index 4f825523c..947cc974c 100644 --- a/crates/ide/src/syntax_highlighting/inject.rs +++ b/crates/ide/src/syntax_highlighting/inject.rs | |||
@@ -1,10 +1,18 @@ | |||
1 | //! "Recursive" Syntax highlighting for code in doctests and fixtures. | 1 | //! "Recursive" Syntax highlighting for code in doctests and fixtures. |
2 | 2 | ||
3 | use hir::Semantics; | 3 | use std::{mem, ops::Range}; |
4 | use ide_db::call_info::ActiveParameter; | ||
5 | use syntax::{ast, AstToken, SyntaxNode, SyntaxToken, TextRange, TextSize}; | ||
6 | 4 | ||
7 | use crate::{Analysis, HlMod, HlRange, HlTag, RootDatabase}; | 5 | use either::Either; |
6 | use hir::{HasAttrs, Semantics}; | ||
7 | use ide_db::{call_info::ActiveParameter, defs::Definition}; | ||
8 | use syntax::{ | ||
9 | ast::{self, AstNode, AttrsOwner, DocCommentsOwner}, | ||
10 | match_ast, AstToken, NodeOrToken, SyntaxNode, SyntaxToken, TextRange, TextSize, | ||
11 | }; | ||
12 | |||
13 | use crate::{ | ||
14 | doc_links::extract_definitions_from_markdown, Analysis, HlMod, HlRange, HlTag, RootDatabase, | ||
15 | }; | ||
8 | 16 | ||
9 | use super::{highlights::Highlights, injector::Injector}; | 17 | use super::{highlights::Highlights, injector::Injector}; |
10 | 18 | ||
@@ -81,70 +89,181 @@ const RUSTDOC_FENCE_TOKENS: &[&'static str] = &[ | |||
81 | "edition2021", | 89 | "edition2021", |
82 | ]; | 90 | ]; |
83 | 91 | ||
84 | /// Injection of syntax highlighting of doctests. | 92 | // Basically an owned dyn AttrsOwner without extra Boxing |
85 | pub(super) fn doc_comment(hl: &mut Highlights, node: &SyntaxNode) { | 93 | struct AttrsOwnerNode { |
86 | let doc_comments = node | 94 | node: SyntaxNode, |
87 | .children_with_tokens() | 95 | } |
88 | .filter_map(|it| it.into_token().and_then(ast::Comment::cast)) | 96 | |
89 | .filter(|it| it.kind().doc.is_some()); | 97 | impl AttrsOwnerNode { |
98 | fn new<N: DocCommentsOwner>(node: N) -> Self { | ||
99 | AttrsOwnerNode { node: node.syntax().clone() } | ||
100 | } | ||
101 | } | ||
102 | |||
103 | impl AttrsOwner for AttrsOwnerNode {} | ||
104 | impl AstNode for AttrsOwnerNode { | ||
105 | fn can_cast(_: syntax::SyntaxKind) -> bool | ||
106 | where | ||
107 | Self: Sized, | ||
108 | { | ||
109 | false | ||
110 | } | ||
111 | fn cast(_: SyntaxNode) -> Option<Self> | ||
112 | where | ||
113 | Self: Sized, | ||
114 | { | ||
115 | None | ||
116 | } | ||
117 | fn syntax(&self) -> &SyntaxNode { | ||
118 | &self.node | ||
119 | } | ||
120 | } | ||
90 | 121 | ||
91 | if !doc_comments.clone().any(|it| it.text().contains(RUSTDOC_FENCE)) { | 122 | fn doc_attributes<'node>( |
92 | return; | 123 | sema: &Semantics<RootDatabase>, |
124 | node: &'node SyntaxNode, | ||
125 | ) -> Option<(AttrsOwnerNode, hir::Attrs, Definition)> { | ||
126 | match_ast! { | ||
127 | match node { | ||
128 | ast::SourceFile(it) => sema.to_def(&it).map(|def| (AttrsOwnerNode::new(it), def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Module(def)))), | ||
129 | ast::Module(it) => sema.to_def(&it).map(|def| (AttrsOwnerNode::new(it), def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Module(def)))), | ||
130 | ast::Fn(it) => sema.to_def(&it).map(|def| (AttrsOwnerNode::new(it), def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Function(def)))), | ||
131 | ast::Struct(it) => sema.to_def(&it).map(|def| (AttrsOwnerNode::new(it), def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Struct(def))))), | ||
132 | ast::Union(it) => sema.to_def(&it).map(|def| (AttrsOwnerNode::new(it), def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Union(def))))), | ||
133 | ast::Enum(it) => sema.to_def(&it).map(|def| (AttrsOwnerNode::new(it), def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Enum(def))))), | ||
134 | ast::Variant(it) => sema.to_def(&it).map(|def| (AttrsOwnerNode::new(it), def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Variant(def)))), | ||
135 | ast::Trait(it) => sema.to_def(&it).map(|def| (AttrsOwnerNode::new(it), def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Trait(def)))), | ||
136 | ast::Static(it) => sema.to_def(&it).map(|def| (AttrsOwnerNode::new(it), def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Static(def)))), | ||
137 | ast::Const(it) => sema.to_def(&it).map(|def| (AttrsOwnerNode::new(it), def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Const(def)))), | ||
138 | ast::TypeAlias(it) => sema.to_def(&it).map(|def| (AttrsOwnerNode::new(it), def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::TypeAlias(def)))), | ||
139 | ast::Impl(it) => sema.to_def(&it).map(|def| (AttrsOwnerNode::new(it), def.attrs(sema.db), Definition::SelfType(def))), | ||
140 | ast::RecordField(it) => sema.to_def(&it).map(|def| (AttrsOwnerNode::new(it), def.attrs(sema.db), Definition::Field(def))), | ||
141 | ast::TupleField(it) => sema.to_def(&it).map(|def| (AttrsOwnerNode::new(it), def.attrs(sema.db), Definition::Field(def))), | ||
142 | ast::MacroRules(it) => sema.to_def(&it).map(|def| (AttrsOwnerNode::new(it), def.attrs(sema.db), Definition::Macro(def))), | ||
143 | // ast::MacroDef(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))), | ||
144 | // ast::Use(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))), | ||
145 | _ => return None | ||
146 | } | ||
93 | } | 147 | } |
148 | } | ||
149 | |||
150 | /// Injection of syntax highlighting of doctests. | ||
151 | pub(super) fn doc_comment(hl: &mut Highlights, sema: &Semantics<RootDatabase>, node: &SyntaxNode) { | ||
152 | let (owner, attributes, def) = match doc_attributes(sema, node) { | ||
153 | Some(it) => it, | ||
154 | None => return, | ||
155 | }; | ||
94 | 156 | ||
95 | let mut inj = Injector::default(); | 157 | let mut inj = Injector::default(); |
96 | inj.add_unmapped("fn doctest() {\n"); | 158 | inj.add_unmapped("fn doctest() {\n"); |
97 | 159 | ||
160 | let attrs_source_map = attributes.source_map(&owner); | ||
161 | |||
98 | let mut is_codeblock = false; | 162 | let mut is_codeblock = false; |
99 | let mut is_doctest = false; | 163 | let mut is_doctest = false; |
100 | 164 | ||
101 | // Replace the original, line-spanning comment ranges by new, only comment-prefix | 165 | // Replace the original, line-spanning comment ranges by new, only comment-prefix |
102 | // spanning comment ranges. | 166 | // spanning comment ranges. |
103 | let mut new_comments = Vec::new(); | 167 | let mut new_comments = Vec::new(); |
104 | for comment in doc_comments { | 168 | let mut intra_doc_links = Vec::new(); |
105 | match comment.text().find(RUSTDOC_FENCE) { | 169 | let mut string; |
106 | Some(idx) => { | 170 | for attr in attributes.by_key("doc").attrs() { |
107 | is_codeblock = !is_codeblock; | 171 | let src = attrs_source_map.source_of(&attr); |
108 | // Check whether code is rust by inspecting fence guards | 172 | let (line, range, prefix) = match &src { |
109 | let guards = &comment.text()[idx + RUSTDOC_FENCE.len()..]; | 173 | Either::Left(it) => { |
110 | let is_rust = | 174 | string = match find_doc_string_in_attr(attr, it) { |
111 | guards.split(',').all(|sub| RUSTDOC_FENCE_TOKENS.contains(&sub.trim())); | 175 | Some(it) => it, |
112 | is_doctest = is_codeblock && is_rust; | 176 | None => continue, |
113 | continue; | 177 | }; |
178 | let text_range = string.syntax().text_range(); | ||
179 | let text_range = TextRange::new( | ||
180 | text_range.start() + TextSize::from(1), | ||
181 | text_range.end() - TextSize::from(1), | ||
182 | ); | ||
183 | let text = string.text(); | ||
184 | (&text[1..text.len() - 1], text_range, "") | ||
114 | } | 185 | } |
115 | None if !is_doctest => continue, | 186 | Either::Right(comment) => { |
116 | None => (), | 187 | (comment.text(), comment.syntax().text_range(), comment.prefix()) |
117 | } | 188 | } |
189 | }; | ||
118 | 190 | ||
119 | let line: &str = comment.text(); | 191 | let mut pos = TextSize::from(prefix.len() as u32); |
120 | let range = comment.syntax().text_range(); | 192 | let mut range_start = range.start(); |
193 | for line in line.split('\n') { | ||
194 | let line_len = TextSize::from(line.len() as u32); | ||
195 | let prev_range_start = { | ||
196 | let next_range_start = range_start + line_len + TextSize::from(1); | ||
197 | mem::replace(&mut range_start, next_range_start) | ||
198 | }; | ||
199 | // only first line has the prefix so take it away for future iterations | ||
200 | let mut pos = mem::take(&mut pos); | ||
121 | 201 | ||
122 | let mut pos = TextSize::of(comment.prefix()); | 202 | match line.find(RUSTDOC_FENCE) { |
123 | // whitespace after comment is ignored | 203 | Some(idx) => { |
124 | if let Some(ws) = line[pos.into()..].chars().next().filter(|c| c.is_whitespace()) { | 204 | is_codeblock = !is_codeblock; |
125 | pos += TextSize::of(ws); | 205 | // Check whether code is rust by inspecting fence guards |
126 | } | 206 | let guards = &line[idx + RUSTDOC_FENCE.len()..]; |
127 | // lines marked with `#` should be ignored in output, we skip the `#` char | 207 | let is_rust = |
128 | if let Some(ws) = line[pos.into()..].chars().next().filter(|&c| c == '#') { | 208 | guards.split(',').all(|sub| RUSTDOC_FENCE_TOKENS.contains(&sub.trim())); |
129 | pos += TextSize::of(ws); | 209 | is_doctest = is_codeblock && is_rust; |
210 | continue; | ||
211 | } | ||
212 | None if !is_doctest => { | ||
213 | intra_doc_links.extend( | ||
214 | extract_definitions_from_markdown(line) | ||
215 | .into_iter() | ||
216 | .filter(|(link, ns, _)| { | ||
217 | validate_intra_doc_link(sema.db, &def, link, *ns) | ||
218 | }) | ||
219 | .map(|(.., Range { start, end })| { | ||
220 | TextRange::at( | ||
221 | prev_range_start + TextSize::from(start as u32), | ||
222 | TextSize::from((end - start) as u32), | ||
223 | ) | ||
224 | }), | ||
225 | ); | ||
226 | continue; | ||
227 | } | ||
228 | None => (), | ||
229 | } | ||
230 | |||
231 | // whitespace after comment is ignored | ||
232 | if let Some(ws) = line[pos.into()..].chars().next().filter(|c| c.is_whitespace()) { | ||
233 | pos += TextSize::of(ws); | ||
234 | } | ||
235 | // lines marked with `#` should be ignored in output, we skip the `#` char | ||
236 | if line[pos.into()..].starts_with('#') { | ||
237 | pos += TextSize::of('#'); | ||
238 | } | ||
239 | |||
240 | new_comments.push(TextRange::at(prev_range_start, pos)); | ||
241 | inj.add(&line[pos.into()..], TextRange::new(pos, line_len) + prev_range_start); | ||
242 | inj.add_unmapped("\n"); | ||
130 | } | 243 | } |
244 | } | ||
131 | 245 | ||
132 | new_comments.push(TextRange::at(range.start(), pos)); | 246 | for range in intra_doc_links { |
247 | hl.add(HlRange { | ||
248 | range, | ||
249 | highlight: HlTag::IntraDocLink | HlMod::Documentation, | ||
250 | binding_hash: None, | ||
251 | }); | ||
252 | } | ||
133 | 253 | ||
134 | inj.add(&line[pos.into()..], TextRange::new(range.start() + pos, range.end())); | 254 | if new_comments.is_empty() { |
135 | inj.add_unmapped("\n"); | 255 | return; // no need to run an analysis on an empty file |
136 | } | 256 | } |
257 | |||
137 | inj.add_unmapped("\n}"); | 258 | inj.add_unmapped("\n}"); |
138 | 259 | ||
139 | let (analysis, tmp_file_id) = Analysis::from_single_file(inj.text().to_string()); | 260 | let (analysis, tmp_file_id) = Analysis::from_single_file(inj.text().to_string()); |
140 | 261 | ||
141 | for h in analysis.with_db(|db| super::highlight(db, tmp_file_id, None, true)).unwrap() { | 262 | for HlRange { range, highlight, binding_hash } in |
142 | for r in inj.map_range_up(h.range) { | 263 | analysis.with_db(|db| super::highlight(db, tmp_file_id, None, true)).unwrap() |
143 | hl.add(HlRange { | 264 | { |
144 | range: r, | 265 | for range in inj.map_range_up(range) { |
145 | highlight: h.highlight | HlMod::Injected, | 266 | hl.add(HlRange { range, highlight: highlight | HlMod::Injected, binding_hash }); |
146 | binding_hash: h.binding_hash, | ||
147 | }); | ||
148 | } | 267 | } |
149 | } | 268 | } |
150 | 269 | ||
@@ -156,3 +275,55 @@ pub(super) fn doc_comment(hl: &mut Highlights, node: &SyntaxNode) { | |||
156 | }); | 275 | }); |
157 | } | 276 | } |
158 | } | 277 | } |
278 | |||
279 | fn find_doc_string_in_attr(attr: &hir::Attr, it: &ast::Attr) -> Option<ast::String> { | ||
280 | match it.literal() { | ||
281 | // #[doc = lit] | ||
282 | Some(lit) => match lit.kind() { | ||
283 | ast::LiteralKind::String(it) => Some(it), | ||
284 | _ => None, | ||
285 | }, | ||
286 | // #[cfg_attr(..., doc = "", ...)] | ||
287 | None => { | ||
288 | // We gotta hunt the string token manually here | ||
289 | let text = attr.string_value()?; | ||
290 | // FIXME: We just pick the first string literal that has the same text as the doc attribute | ||
291 | // This means technically we might highlight the wrong one | ||
292 | it.syntax() | ||
293 | .descendants_with_tokens() | ||
294 | .filter_map(NodeOrToken::into_token) | ||
295 | .filter_map(ast::String::cast) | ||
296 | .find(|string| { | ||
297 | string.text().get(1..string.text().len() - 1).map_or(false, |it| it == text) | ||
298 | }) | ||
299 | } | ||
300 | } | ||
301 | } | ||
302 | |||
303 | fn validate_intra_doc_link( | ||
304 | db: &RootDatabase, | ||
305 | def: &Definition, | ||
306 | link: &str, | ||
307 | ns: Option<hir::Namespace>, | ||
308 | ) -> bool { | ||
309 | match def { | ||
310 | Definition::ModuleDef(def) => match def { | ||
311 | hir::ModuleDef::Module(it) => it.resolve_doc_path(db, &link, ns), | ||
312 | hir::ModuleDef::Function(it) => it.resolve_doc_path(db, &link, ns), | ||
313 | hir::ModuleDef::Adt(it) => it.resolve_doc_path(db, &link, ns), | ||
314 | hir::ModuleDef::Variant(it) => it.resolve_doc_path(db, &link, ns), | ||
315 | hir::ModuleDef::Const(it) => it.resolve_doc_path(db, &link, ns), | ||
316 | hir::ModuleDef::Static(it) => it.resolve_doc_path(db, &link, ns), | ||
317 | hir::ModuleDef::Trait(it) => it.resolve_doc_path(db, &link, ns), | ||
318 | hir::ModuleDef::TypeAlias(it) => it.resolve_doc_path(db, &link, ns), | ||
319 | hir::ModuleDef::BuiltinType(_) => None, | ||
320 | }, | ||
321 | Definition::Macro(it) => it.resolve_doc_path(db, &link, ns), | ||
322 | Definition::Field(it) => it.resolve_doc_path(db, &link, ns), | ||
323 | Definition::SelfType(_) | ||
324 | | Definition::Local(_) | ||
325 | | Definition::GenericParam(_) | ||
326 | | Definition::Label(_) => None, | ||
327 | } | ||
328 | .is_some() | ||
329 | } | ||
diff --git a/crates/ide/src/syntax_highlighting/tags.rs b/crates/ide/src/syntax_highlighting/tags.rs index 3c02fdb11..ce46e5127 100644 --- a/crates/ide/src/syntax_highlighting/tags.rs +++ b/crates/ide/src/syntax_highlighting/tags.rs | |||
@@ -18,19 +18,20 @@ pub struct HlMods(u32); | |||
18 | pub enum HlTag { | 18 | pub enum HlTag { |
19 | Symbol(SymbolKind), | 19 | Symbol(SymbolKind), |
20 | 20 | ||
21 | Attribute, | ||
21 | BoolLiteral, | 22 | BoolLiteral, |
22 | BuiltinType, | 23 | BuiltinType, |
23 | ByteLiteral, | 24 | ByteLiteral, |
24 | CharLiteral, | 25 | CharLiteral, |
25 | NumericLiteral, | ||
26 | StringLiteral, | ||
27 | Attribute, | ||
28 | Comment, | 26 | Comment, |
29 | EscapeSequence, | 27 | EscapeSequence, |
30 | FormatSpecifier, | 28 | FormatSpecifier, |
29 | IntraDocLink, | ||
31 | Keyword, | 30 | Keyword, |
32 | Punctuation(HlPunct), | 31 | NumericLiteral, |
33 | Operator, | 32 | Operator, |
33 | Punctuation(HlPunct), | ||
34 | StringLiteral, | ||
34 | UnresolvedReference, | 35 | UnresolvedReference, |
35 | 36 | ||
36 | // For things which don't have a specific highlight. | 37 | // For things which don't have a specific highlight. |
@@ -116,6 +117,7 @@ impl HlTag { | |||
116 | HlTag::Comment => "comment", | 117 | HlTag::Comment => "comment", |
117 | HlTag::EscapeSequence => "escape_sequence", | 118 | HlTag::EscapeSequence => "escape_sequence", |
118 | HlTag::FormatSpecifier => "format_specifier", | 119 | HlTag::FormatSpecifier => "format_specifier", |
120 | HlTag::IntraDocLink => "intra_doc_link", | ||
119 | HlTag::Keyword => "keyword", | 121 | HlTag::Keyword => "keyword", |
120 | HlTag::Punctuation(punct) => match punct { | 122 | HlTag::Punctuation(punct) => match punct { |
121 | HlPunct::Bracket => "bracket", | 123 | HlPunct::Bracket => "bracket", |
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html b/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html index d421a7803..60c7518af 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html | |||
@@ -7,6 +7,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
7 | .label { color: #DFAF8F; font-style: italic; } | 7 | .label { color: #DFAF8F; font-style: italic; } |
8 | .comment { color: #7F9F7F; } | 8 | .comment { color: #7F9F7F; } |
9 | .documentation { color: #629755; } | 9 | .documentation { color: #629755; } |
10 | .intra_doc_link { color: #A9C577; } | ||
10 | .injected { opacity: 0.65 ; } | 11 | .injected { opacity: 0.65 ; } |
11 | .struct, .enum { color: #7CB8BB; } | 12 | .struct, .enum { color: #7CB8BB; } |
12 | .enum_variant { color: #BDE0F3; } | 13 | .enum_variant { color: #BDE0F3; } |
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html index 5e877df88..5d802a647 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html | |||
@@ -7,6 +7,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
7 | .label { color: #DFAF8F; font-style: italic; } | 7 | .label { color: #DFAF8F; font-style: italic; } |
8 | .comment { color: #7F9F7F; } | 8 | .comment { color: #7F9F7F; } |
9 | .documentation { color: #629755; } | 9 | .documentation { color: #629755; } |
10 | .intra_doc_link { color: #A9C577; } | ||
10 | .injected { opacity: 0.65 ; } | 11 | .injected { opacity: 0.65 ; } |
11 | .struct, .enum { color: #7CB8BB; } | 12 | .struct, .enum { color: #7CB8BB; } |
12 | .enum_variant { color: #BDE0F3; } | 13 | .enum_variant { color: #BDE0F3; } |
@@ -81,7 +82,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
81 | <span class="comment documentation">/// </span><span class="comment injected"> comment */</span> | 82 | <span class="comment documentation">/// </span><span class="comment injected"> comment */</span> |
82 | <span class="comment documentation">///</span> | 83 | <span class="comment documentation">///</span> |
83 | <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="variable declaration injected">multi_line_string</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="string_literal injected">"Foo</span> | 84 | <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="variable declaration injected">multi_line_string</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="string_literal injected">"Foo</span> |
84 | <span class="comment documentation">/// </span><span class="string_literal injected"> bar</span> | 85 | <span class="comment documentation">/// </span><span class="string_literal injected"> bar</span><span class="escape_sequence injected">\n</span> |
85 | <span class="comment documentation">/// </span><span class="string_literal injected"> "</span><span class="semicolon injected">;</span> | 86 | <span class="comment documentation">/// </span><span class="string_literal injected"> "</span><span class="semicolon injected">;</span> |
86 | <span class="comment documentation">///</span> | 87 | <span class="comment documentation">///</span> |
87 | <span class="comment documentation">/// ```</span> | 88 | <span class="comment documentation">/// ```</span> |
@@ -98,6 +99,11 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
98 | <span class="brace">}</span> | 99 | <span class="brace">}</span> |
99 | <span class="brace">}</span> | 100 | <span class="brace">}</span> |
100 | 101 | ||
102 | <span class="comment documentation">/// </span><span class="intra_doc_link documentation">[`Foo`](Foo)</span><span class="comment documentation"> is a struct</span> | ||
103 | <span class="comment documentation">/// </span><span class="intra_doc_link documentation">[`all_the_links`](all_the_links)</span><span class="comment documentation"> is this function</span> | ||
104 | <span class="comment documentation">/// [`noop`](noop) is a macro below</span> | ||
105 | <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration">all_the_links</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> | ||
106 | |||
101 | <span class="comment documentation">/// ```</span> | 107 | <span class="comment documentation">/// ```</span> |
102 | <span class="comment documentation">/// </span><span class="macro injected">noop!</span><span class="parenthesis injected">(</span><span class="numeric_literal injected">1</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span> | 108 | <span class="comment documentation">/// </span><span class="macro injected">noop!</span><span class="parenthesis injected">(</span><span class="numeric_literal injected">1</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span> |
103 | <span class="comment documentation">/// ```</span> | 109 | <span class="comment documentation">/// ```</span> |
@@ -105,4 +111,36 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
105 | <span class="parenthesis">(</span><span class="punctuation">$</span>expr<span class="colon">:</span>expr<span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">></span> <span class="brace">{</span> | 111 | <span class="parenthesis">(</span><span class="punctuation">$</span>expr<span class="colon">:</span>expr<span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">></span> <span class="brace">{</span> |
106 | <span class="punctuation">$</span>expr | 112 | <span class="punctuation">$</span>expr |
107 | <span class="brace">}</span> | 113 | <span class="brace">}</span> |
108 | <span class="brace">}</span></code></pre> \ No newline at end of file | 114 | <span class="brace">}</span> |
115 | |||
116 | <span class="comment documentation">/// ```rust</span> | ||
117 | <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="punctuation injected">_</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="function injected">example</span><span class="parenthesis injected">(</span><span class="operator injected">&</span><span class="bracket injected">[</span><span class="numeric_literal injected">1</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">2</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">3</span><span class="bracket injected">]</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span> | ||
118 | <span class="comment documentation">/// ```</span> | ||
119 | <span class="comment documentation">///</span> | ||
120 | <span class="comment documentation">/// ```</span> | ||
121 | <span class="comment documentation">/// </span><span class="keyword control injected">loop</span><span class="none injected"> </span><span class="brace injected">{</span><span class="brace injected">}</span> | ||
122 | <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">cfg_attr</span><span class="parenthesis attribute">(</span><span class="attribute attribute">not</span><span class="parenthesis attribute">(</span><span class="attribute attribute">feature </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"false"</span><span class="parenthesis attribute">)</span><span class="comma attribute">,</span><span class="attribute attribute"> doc </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"</span><span class="keyword control injected">loop</span><span class="none injected"> </span><span class="brace injected">{</span><span class="brace injected">}</span><span class="string_literal attribute">"</span><span class="parenthesis attribute">)</span><span class="attribute attribute">]</span> | ||
123 | <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">doc</span><span class="attribute attribute"> </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"</span><span class="keyword control injected">loop</span><span class="none injected"> </span><span class="brace injected">{</span><span class="brace injected">}</span><span class="string_literal attribute">"</span><span class="attribute attribute">]</span> | ||
124 | <span class="comment documentation">/// ```</span> | ||
125 | <span class="comment documentation">///</span> | ||
126 | <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">cfg_attr</span><span class="parenthesis attribute">(</span><span class="attribute attribute">feature </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"alloc"</span><span class="comma attribute">,</span><span class="attribute attribute"> doc </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"```rust"</span><span class="parenthesis attribute">)</span><span class="attribute attribute">]</span> | ||
127 | <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">cfg_attr</span><span class="parenthesis attribute">(</span><span class="attribute attribute">not</span><span class="parenthesis attribute">(</span><span class="attribute attribute">feature </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"alloc"</span><span class="parenthesis attribute">)</span><span class="comma attribute">,</span><span class="attribute attribute"> doc </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"```ignore"</span><span class="parenthesis attribute">)</span><span class="attribute attribute">]</span> | ||
128 | <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="punctuation injected">_</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="function injected">example</span><span class="parenthesis injected">(</span><span class="operator injected">&</span><span class="none injected">alloc::</span><span class="macro injected">vec!</span><span class="bracket injected">[</span><span class="numeric_literal injected">1</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">2</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">3</span><span class="bracket injected">]</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span> | ||
129 | <span class="comment documentation">/// ```</span> | ||
130 | <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration">mix_and_match</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> | ||
131 | |||
132 | <span class="comment documentation">/** | ||
133 | It is beyond me why you'd use these when you got /// | ||
134 | ```rust | ||
135 | </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="punctuation injected">_</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="function injected">example</span><span class="parenthesis injected">(</span><span class="operator injected">&</span><span class="bracket injected">[</span><span class="numeric_literal injected">1</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">2</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">3</span><span class="bracket injected">]</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span><span class="comment documentation"> | ||
136 | ``` | ||
137 | */</span> | ||
138 | <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration">block_comments</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> | ||
139 | |||
140 | <span class="comment documentation">/** | ||
141 | Really, I don't get it | ||
142 | ```rust | ||
143 | </span><span class="comment documentation"> </span><span class="none injected"> </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="punctuation injected">_</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="function injected">example</span><span class="parenthesis injected">(</span><span class="operator injected">&</span><span class="bracket injected">[</span><span class="numeric_literal injected">1</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">2</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">3</span><span class="bracket injected">]</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span><span class="comment documentation"> | ||
144 | ``` | ||
145 | */</span> | ||
146 | <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration">block_comments2</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span></code></pre> \ No newline at end of file | ||
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html b/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html index 6f7a7ffff..4e312765c 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html | |||
@@ -7,6 +7,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
7 | .label { color: #DFAF8F; font-style: italic; } | 7 | .label { color: #DFAF8F; font-style: italic; } |
8 | .comment { color: #7F9F7F; } | 8 | .comment { color: #7F9F7F; } |
9 | .documentation { color: #629755; } | 9 | .documentation { color: #629755; } |
10 | .intra_doc_link { color: #A9C577; } | ||
10 | .injected { opacity: 0.65 ; } | 11 | .injected { opacity: 0.65 ; } |
11 | .struct, .enum { color: #7CB8BB; } | 12 | .struct, .enum { color: #7CB8BB; } |
12 | .enum_variant { color: #BDE0F3; } | 13 | .enum_variant { color: #BDE0F3; } |
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html b/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html index 753b535b5..57dfe7509 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html | |||
@@ -7,6 +7,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
7 | .label { color: #DFAF8F; font-style: italic; } | 7 | .label { color: #DFAF8F; font-style: italic; } |
8 | .comment { color: #7F9F7F; } | 8 | .comment { color: #7F9F7F; } |
9 | .documentation { color: #629755; } | 9 | .documentation { color: #629755; } |
10 | .intra_doc_link { color: #A9C577; } | ||
10 | .injected { opacity: 0.65 ; } | 11 | .injected { opacity: 0.65 ; } |
11 | .struct, .enum { color: #7CB8BB; } | 12 | .struct, .enum { color: #7CB8BB; } |
12 | .enum_variant { color: #BDE0F3; } | 13 | .enum_variant { color: #BDE0F3; } |
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html index 66d80c4b6..75dbd0f14 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html | |||
@@ -7,6 +7,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
7 | .label { color: #DFAF8F; font-style: italic; } | 7 | .label { color: #DFAF8F; font-style: italic; } |
8 | .comment { color: #7F9F7F; } | 8 | .comment { color: #7F9F7F; } |
9 | .documentation { color: #629755; } | 9 | .documentation { color: #629755; } |
10 | .intra_doc_link { color: #A9C577; } | ||
10 | .injected { opacity: 0.65 ; } | 11 | .injected { opacity: 0.65 ; } |
11 | .struct, .enum { color: #7CB8BB; } | 12 | .struct, .enum { color: #7CB8BB; } |
12 | .enum_variant { color: #BDE0F3; } | 13 | .enum_variant { color: #BDE0F3; } |
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html index 036cb6c11..423256a20 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html | |||
@@ -7,6 +7,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
7 | .label { color: #DFAF8F; font-style: italic; } | 7 | .label { color: #DFAF8F; font-style: italic; } |
8 | .comment { color: #7F9F7F; } | 8 | .comment { color: #7F9F7F; } |
9 | .documentation { color: #629755; } | 9 | .documentation { color: #629755; } |
10 | .intra_doc_link { color: #A9C577; } | ||
10 | .injected { opacity: 0.65 ; } | 11 | .injected { opacity: 0.65 ; } |
11 | .struct, .enum { color: #7CB8BB; } | 12 | .struct, .enum { color: #7CB8BB; } |
12 | .enum_variant { color: #BDE0F3; } | 13 | .enum_variant { color: #BDE0F3; } |
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlighting.html b/crates/ide/src/syntax_highlighting/test_data/highlighting.html index 2f983c0b8..fffe8c0f5 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlighting.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlighting.html | |||
@@ -7,6 +7,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
7 | .label { color: #DFAF8F; font-style: italic; } | 7 | .label { color: #DFAF8F; font-style: italic; } |
8 | .comment { color: #7F9F7F; } | 8 | .comment { color: #7F9F7F; } |
9 | .documentation { color: #629755; } | 9 | .documentation { color: #629755; } |
10 | .intra_doc_link { color: #A9C577; } | ||
10 | .injected { opacity: 0.65 ; } | 11 | .injected { opacity: 0.65 ; } |
11 | .struct, .enum { color: #7CB8BB; } | 12 | .struct, .enum { color: #7CB8BB; } |
12 | .enum_variant { color: #BDE0F3; } | 13 | .enum_variant { color: #BDE0F3; } |
diff --git a/crates/ide/src/syntax_highlighting/test_data/injection.html b/crates/ide/src/syntax_highlighting/test_data/injection.html index 78dfec951..34d8deb68 100644 --- a/crates/ide/src/syntax_highlighting/test_data/injection.html +++ b/crates/ide/src/syntax_highlighting/test_data/injection.html | |||
@@ -7,6 +7,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
7 | .label { color: #DFAF8F; font-style: italic; } | 7 | .label { color: #DFAF8F; font-style: italic; } |
8 | .comment { color: #7F9F7F; } | 8 | .comment { color: #7F9F7F; } |
9 | .documentation { color: #629755; } | 9 | .documentation { color: #629755; } |
10 | .intra_doc_link { color: #A9C577; } | ||
10 | .injected { opacity: 0.65 ; } | 11 | .injected { opacity: 0.65 ; } |
11 | .struct, .enum { color: #7CB8BB; } | 12 | .struct, .enum { color: #7CB8BB; } |
12 | .enum_variant { color: #BDE0F3; } | 13 | .enum_variant { color: #BDE0F3; } |
diff --git a/crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html b/crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html index e64f2e5e9..d9ca3a4c4 100644 --- a/crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html +++ b/crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html | |||
@@ -7,6 +7,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
7 | .label { color: #DFAF8F; font-style: italic; } | 7 | .label { color: #DFAF8F; font-style: italic; } |
8 | .comment { color: #7F9F7F; } | 8 | .comment { color: #7F9F7F; } |
9 | .documentation { color: #629755; } | 9 | .documentation { color: #629755; } |
10 | .intra_doc_link { color: #A9C577; } | ||
10 | .injected { opacity: 0.65 ; } | 11 | .injected { opacity: 0.65 ; } |
11 | .struct, .enum { color: #7CB8BB; } | 12 | .struct, .enum { color: #7CB8BB; } |
12 | .enum_variant { color: #BDE0F3; } | 13 | .enum_variant { color: #BDE0F3; } |
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs index 9d0cd1af5..7b2922b0d 100644 --- a/crates/ide/src/syntax_highlighting/tests.rs +++ b/crates/ide/src/syntax_highlighting/tests.rs | |||
@@ -468,7 +468,7 @@ fn main() { | |||
468 | } | 468 | } |
469 | 469 | ||
470 | #[test] | 470 | #[test] |
471 | fn test_highlight_doctest() { | 471 | fn test_highlight_doc_comment() { |
472 | check_highlighting( | 472 | check_highlighting( |
473 | r#" | 473 | r#" |
474 | /// ``` | 474 | /// ``` |
@@ -516,7 +516,7 @@ impl Foo { | |||
516 | /// comment */ | 516 | /// comment */ |
517 | /// | 517 | /// |
518 | /// let multi_line_string = "Foo | 518 | /// let multi_line_string = "Foo |
519 | /// bar | 519 | /// bar\n |
520 | /// "; | 520 | /// "; |
521 | /// | 521 | /// |
522 | /// ``` | 522 | /// ``` |
@@ -533,6 +533,11 @@ impl Foo { | |||
533 | } | 533 | } |
534 | } | 534 | } |
535 | 535 | ||
536 | /// [`Foo`](Foo) is a struct | ||
537 | /// [`all_the_links`](all_the_links) is this function | ||
538 | /// [`noop`](noop) is a macro below | ||
539 | pub fn all_the_links() {} | ||
540 | |||
536 | /// ``` | 541 | /// ``` |
537 | /// noop!(1); | 542 | /// noop!(1); |
538 | /// ``` | 543 | /// ``` |
@@ -541,6 +546,38 @@ macro_rules! noop { | |||
541 | $expr | 546 | $expr |
542 | } | 547 | } |
543 | } | 548 | } |
549 | |||
550 | /// ```rust | ||
551 | /// let _ = example(&[1, 2, 3]); | ||
552 | /// ``` | ||
553 | /// | ||
554 | /// ``` | ||
555 | /// loop {} | ||
556 | #[cfg_attr(not(feature = "false"), doc = "loop {}")] | ||
557 | #[doc = "loop {}"] | ||
558 | /// ``` | ||
559 | /// | ||
560 | #[cfg_attr(feature = "alloc", doc = "```rust")] | ||
561 | #[cfg_attr(not(feature = "alloc"), doc = "```ignore")] | ||
562 | /// let _ = example(&alloc::vec![1, 2, 3]); | ||
563 | /// ``` | ||
564 | pub fn mix_and_match() {} | ||
565 | |||
566 | /** | ||
567 | It is beyond me why you'd use these when you got /// | ||
568 | ```rust | ||
569 | let _ = example(&[1, 2, 3]); | ||
570 | ``` | ||
571 | */ | ||
572 | pub fn block_comments() {} | ||
573 | |||
574 | /** | ||
575 | Really, I don't get it | ||
576 | ```rust | ||
577 | let _ = example(&[1, 2, 3]); | ||
578 | ``` | ||
579 | */ | ||
580 | pub fn block_comments2() {} | ||
544 | "# | 581 | "# |
545 | .trim(), | 582 | .trim(), |
546 | expect_file!["./test_data/highlight_doctest.html"], | 583 | expect_file!["./test_data/highlight_doctest.html"], |
diff --git a/crates/ide_completion/src/lib.rs b/crates/ide_completion/src/lib.rs index 5b7ad38d5..d9ea7b7ea 100644 --- a/crates/ide_completion/src/lib.rs +++ b/crates/ide_completion/src/lib.rs | |||
@@ -255,7 +255,7 @@ fn foo() { | |||
255 | bar.fo$0; | 255 | bar.fo$0; |
256 | } | 256 | } |
257 | "#, | 257 | "#, |
258 | DetailAndDocumentation { detail: "fn(&self)", documentation: " Do the foo" }, | 258 | DetailAndDocumentation { detail: "fn(&self)", documentation: "Do the foo" }, |
259 | ); | 259 | ); |
260 | } | 260 | } |
261 | 261 | ||
diff --git a/crates/ide_db/src/apply_change.rs b/crates/ide_db/src/apply_change.rs index 104ee113f..9b590d919 100644 --- a/crates/ide_db/src/apply_change.rs +++ b/crates/ide_db/src/apply_change.rs | |||
@@ -148,7 +148,7 @@ impl RootDatabase { | |||
148 | hir::db::HygieneFrameQuery | 148 | hir::db::HygieneFrameQuery |
149 | 149 | ||
150 | // DefDatabase | 150 | // DefDatabase |
151 | hir::db::ItemTreeQuery | 151 | hir::db::FileItemTreeQuery |
152 | hir::db::BlockDefMapQuery | 152 | hir::db::BlockDefMapQuery |
153 | hir::db::CrateDefMapQueryQuery | 153 | hir::db::CrateDefMapQueryQuery |
154 | hir::db::StructDataQuery | 154 | hir::db::StructDataQuery |
diff --git a/crates/ide_db/src/call_info.rs b/crates/ide_db/src/call_info.rs index d4016973c..7e26c3ccf 100644 --- a/crates/ide_db/src/call_info.rs +++ b/crates/ide_db/src/call_info.rs | |||
@@ -53,15 +53,15 @@ pub fn call_info(db: &RootDatabase, position: FilePosition) -> Option<CallInfo> | |||
53 | 53 | ||
54 | match callable.kind() { | 54 | match callable.kind() { |
55 | hir::CallableKind::Function(func) => { | 55 | hir::CallableKind::Function(func) => { |
56 | res.doc = func.docs(db).map(|it| it.as_str().to_string()); | 56 | res.doc = func.docs(db).map(|it| it.into()); |
57 | format_to!(res.signature, "fn {}", func.name(db)); | 57 | format_to!(res.signature, "fn {}", func.name(db)); |
58 | } | 58 | } |
59 | hir::CallableKind::TupleStruct(strukt) => { | 59 | hir::CallableKind::TupleStruct(strukt) => { |
60 | res.doc = strukt.docs(db).map(|it| it.as_str().to_string()); | 60 | res.doc = strukt.docs(db).map(|it| it.into()); |
61 | format_to!(res.signature, "struct {}", strukt.name(db)); | 61 | format_to!(res.signature, "struct {}", strukt.name(db)); |
62 | } | 62 | } |
63 | hir::CallableKind::TupleEnumVariant(variant) => { | 63 | hir::CallableKind::TupleEnumVariant(variant) => { |
64 | res.doc = variant.docs(db).map(|it| it.as_str().to_string()); | 64 | res.doc = variant.docs(db).map(|it| it.into()); |
65 | format_to!( | 65 | format_to!( |
66 | res.signature, | 66 | res.signature, |
67 | "enum {}::{}", | 67 | "enum {}::{}", |
diff --git a/crates/ide_db/src/call_info/tests.rs b/crates/ide_db/src/call_info/tests.rs index 9f84c253c..75ab3eb6e 100644 --- a/crates/ide_db/src/call_info/tests.rs +++ b/crates/ide_db/src/call_info/tests.rs | |||
@@ -220,11 +220,11 @@ fn bar() { | |||
220 | } | 220 | } |
221 | "#, | 221 | "#, |
222 | expect![[r#" | 222 | expect![[r#" |
223 | test | 223 | test |
224 | ------ | 224 | ------ |
225 | fn foo(j: u32) -> u32 | 225 | fn foo(j: u32) -> u32 |
226 | (<j: u32>) | 226 | (<j: u32>) |
227 | "#]], | 227 | "#]], |
228 | ); | 228 | ); |
229 | } | 229 | } |
230 | 230 | ||
@@ -249,19 +249,19 @@ pub fn do() { | |||
249 | add_one($0 | 249 | add_one($0 |
250 | }"#, | 250 | }"#, |
251 | expect![[r##" | 251 | expect![[r##" |
252 | Adds one to the number given. | 252 | Adds one to the number given. |
253 | 253 | ||
254 | # Examples | 254 | # Examples |
255 | 255 | ||
256 | ``` | 256 | ``` |
257 | let five = 5; | 257 | let five = 5; |
258 | 258 | ||
259 | assert_eq!(6, my_crate::add_one(5)); | 259 | assert_eq!(6, my_crate::add_one(5)); |
260 | ``` | 260 | ``` |
261 | ------ | 261 | ------ |
262 | fn add_one(x: i32) -> i32 | 262 | fn add_one(x: i32) -> i32 |
263 | (<x: i32>) | 263 | (<x: i32>) |
264 | "##]], | 264 | "##]], |
265 | ); | 265 | ); |
266 | } | 266 | } |
267 | 267 | ||
@@ -291,19 +291,19 @@ pub fn do_it() { | |||
291 | } | 291 | } |
292 | "#, | 292 | "#, |
293 | expect![[r##" | 293 | expect![[r##" |
294 | Adds one to the number given. | 294 | Adds one to the number given. |
295 | 295 | ||
296 | # Examples | 296 | # Examples |
297 | 297 | ||
298 | ``` | 298 | ``` |
299 | let five = 5; | 299 | let five = 5; |
300 | 300 | ||
301 | assert_eq!(6, my_crate::add_one(5)); | 301 | assert_eq!(6, my_crate::add_one(5)); |
302 | ``` | 302 | ``` |
303 | ------ | 303 | ------ |
304 | fn add_one(x: i32) -> i32 | 304 | fn add_one(x: i32) -> i32 |
305 | (<x: i32>) | 305 | (<x: i32>) |
306 | "##]], | 306 | "##]], |
307 | ); | 307 | ); |
308 | } | 308 | } |
309 | 309 | ||
@@ -335,13 +335,13 @@ pub fn foo(mut r: WriteHandler<()>) { | |||
335 | } | 335 | } |
336 | "#, | 336 | "#, |
337 | expect![[r#" | 337 | expect![[r#" |
338 | Method is called when writer finishes. | 338 | Method is called when writer finishes. |
339 | 339 | ||
340 | By default this method stops actor's `Context`. | 340 | By default this method stops actor's `Context`. |
341 | ------ | 341 | ------ |
342 | fn finished(&mut self, ctx: &mut {unknown}) | 342 | fn finished(&mut self, ctx: &mut {unknown}) |
343 | (<ctx: &mut {unknown}>) | 343 | (<ctx: &mut {unknown}>) |
344 | "#]], | 344 | "#]], |
345 | ); | 345 | ); |
346 | } | 346 | } |
347 | 347 | ||
@@ -389,11 +389,11 @@ fn main() { | |||
389 | } | 389 | } |
390 | "#, | 390 | "#, |
391 | expect![[r#" | 391 | expect![[r#" |
392 | A cool tuple struct | 392 | A cool tuple struct |
393 | ------ | 393 | ------ |
394 | struct S(u32, i32) | 394 | struct S(u32, i32) |
395 | (u32, <i32>) | 395 | (u32, <i32>) |
396 | "#]], | 396 | "#]], |
397 | ); | 397 | ); |
398 | } | 398 | } |
399 | 399 | ||
@@ -431,11 +431,11 @@ fn main() { | |||
431 | } | 431 | } |
432 | "#, | 432 | "#, |
433 | expect![[r#" | 433 | expect![[r#" |
434 | A Variant | 434 | A Variant |
435 | ------ | 435 | ------ |
436 | enum E::A(i32) | 436 | enum E::A(i32) |
437 | (<i32>) | 437 | (<i32>) |
438 | "#]], | 438 | "#]], |
439 | ); | 439 | ); |
440 | } | 440 | } |
441 | 441 | ||
diff --git a/crates/mbe/src/tests.rs b/crates/mbe/src/tests.rs index 25c374b9b..6da18ecf4 100644 --- a/crates/mbe/src/tests.rs +++ b/crates/mbe/src/tests.rs | |||
@@ -1,1775 +1,14 @@ | |||
1 | mod expand; | ||
2 | mod rule; | ||
3 | |||
1 | use std::fmt::Write; | 4 | use std::fmt::Write; |
2 | 5 | ||
3 | use ::parser::FragmentKind; | 6 | use ::parser::FragmentKind; |
4 | use syntax::{ | 7 | use syntax::{ast, AstNode, NodeOrToken, SyntaxNode, WalkEvent}; |
5 | ast, AstNode, NodeOrToken, | ||
6 | SyntaxKind::{ERROR, IDENT}, | ||
7 | SyntaxNode, WalkEvent, T, | ||
8 | }; | ||
9 | use test_utils::assert_eq_text; | 8 | use test_utils::assert_eq_text; |
10 | 9 | ||
11 | use super::*; | 10 | use super::*; |
12 | 11 | ||
13 | mod rule_parsing { | ||
14 | use syntax::{ast, AstNode}; | ||
15 | |||
16 | use crate::ast_to_token_tree; | ||
17 | |||
18 | use super::*; | ||
19 | |||
20 | #[test] | ||
21 | fn test_valid_arms() { | ||
22 | fn check(macro_body: &str) { | ||
23 | let m = parse_macro_arm(macro_body); | ||
24 | m.unwrap(); | ||
25 | } | ||
26 | |||
27 | check("($i:ident) => ()"); | ||
28 | check("($($i:ident)*) => ($_)"); | ||
29 | check("($($true:ident)*) => ($true)"); | ||
30 | check("($($false:ident)*) => ($false)"); | ||
31 | check("($) => ($)"); | ||
32 | } | ||
33 | |||
34 | #[test] | ||
35 | fn test_invalid_arms() { | ||
36 | fn check(macro_body: &str, err: ParseError) { | ||
37 | let m = parse_macro_arm(macro_body); | ||
38 | assert_eq!(m, Err(err)); | ||
39 | } | ||
40 | check("invalid", ParseError::Expected("expected subtree".into())); | ||
41 | |||
42 | check("$i:ident => ()", ParseError::Expected("expected subtree".into())); | ||
43 | check("($i:ident) ()", ParseError::Expected("expected `=`".into())); | ||
44 | check("($($i:ident)_) => ()", ParseError::InvalidRepeat); | ||
45 | |||
46 | check("($i) => ($i)", ParseError::UnexpectedToken("bad fragment specifier 1".into())); | ||
47 | check("($i:) => ($i)", ParseError::UnexpectedToken("bad fragment specifier 1".into())); | ||
48 | } | ||
49 | |||
50 | fn parse_macro_arm(arm_definition: &str) -> Result<crate::MacroRules, ParseError> { | ||
51 | let macro_definition = format!(" macro_rules! m {{ {} }} ", arm_definition); | ||
52 | let source_file = ast::SourceFile::parse(¯o_definition).ok().unwrap(); | ||
53 | let macro_definition = | ||
54 | source_file.syntax().descendants().find_map(ast::MacroRules::cast).unwrap(); | ||
55 | |||
56 | let (definition_tt, _) = | ||
57 | ast_to_token_tree(¯o_definition.token_tree().unwrap()).unwrap(); | ||
58 | crate::MacroRules::parse(&definition_tt) | ||
59 | } | ||
60 | } | ||
61 | |||
62 | // Good first issue (although a slightly challenging one): | ||
63 | // | ||
64 | // * Pick a random test from here | ||
65 | // https://github.com/intellij-rust/intellij-rust/blob/c4e9feee4ad46e7953b1948c112533360b6087bb/src/test/kotlin/org/rust/lang/core/macros/RsMacroExpansionTest.kt | ||
66 | // * Port the test to rust and add it to this module | ||
67 | // * Make it pass :-) | ||
68 | |||
69 | #[test] | ||
70 | fn test_token_id_shift() { | ||
71 | let expansion = parse_macro( | ||
72 | r#" | ||
73 | macro_rules! foobar { | ||
74 | ($e:ident) => { foo bar $e } | ||
75 | } | ||
76 | "#, | ||
77 | ) | ||
78 | .expand_tt("foobar!(baz);"); | ||
79 | |||
80 | fn get_id(t: &tt::TokenTree) -> Option<u32> { | ||
81 | if let tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) = t { | ||
82 | return Some(ident.id.0); | ||
83 | } | ||
84 | None | ||
85 | } | ||
86 | |||
87 | assert_eq!(expansion.token_trees.len(), 3); | ||
88 | // {($e:ident) => { foo bar $e }} | ||
89 | // 012345 67 8 9 T 12 | ||
90 | assert_eq!(get_id(&expansion.token_trees[0]), Some(9)); | ||
91 | assert_eq!(get_id(&expansion.token_trees[1]), Some(10)); | ||
92 | |||
93 | // The input args of macro call include parentheses: | ||
94 | // (baz) | ||
95 | // So baz should be 12+1+1 | ||
96 | assert_eq!(get_id(&expansion.token_trees[2]), Some(14)); | ||
97 | } | ||
98 | |||
99 | #[test] | ||
100 | fn test_token_map() { | ||
101 | let expanded = parse_macro( | ||
102 | r#" | ||
103 | macro_rules! foobar { | ||
104 | ($e:ident) => { fn $e() {} } | ||
105 | } | ||
106 | "#, | ||
107 | ) | ||
108 | .expand_tt("foobar!(baz);"); | ||
109 | |||
110 | let (node, token_map) = token_tree_to_syntax_node(&expanded, FragmentKind::Items).unwrap(); | ||
111 | let content = node.syntax_node().to_string(); | ||
112 | |||
113 | let get_text = |id, kind| -> String { | ||
114 | content[token_map.range_by_token(id).unwrap().by_kind(kind).unwrap()].to_string() | ||
115 | }; | ||
116 | |||
117 | assert_eq!(expanded.token_trees.len(), 4); | ||
118 | // {($e:ident) => { fn $e() {} }} | ||
119 | // 012345 67 8 9 T12 3 | ||
120 | |||
121 | assert_eq!(get_text(tt::TokenId(9), IDENT), "fn"); | ||
122 | assert_eq!(get_text(tt::TokenId(12), T!['(']), "("); | ||
123 | assert_eq!(get_text(tt::TokenId(13), T!['{']), "{"); | ||
124 | } | ||
125 | |||
126 | #[test] | ||
127 | fn test_convert_tt() { | ||
128 | parse_macro(r#" | ||
129 | macro_rules! impl_froms { | ||
130 | ($e:ident: $($v:ident),*) => { | ||
131 | $( | ||
132 | impl From<$v> for $e { | ||
133 | fn from(it: $v) -> $e { | ||
134 | $e::$v(it) | ||
135 | } | ||
136 | } | ||
137 | )* | ||
138 | } | ||
139 | } | ||
140 | "#) | ||
141 | .assert_expand_tt( | ||
142 | "impl_froms!(TokenTree: Leaf, Subtree);", | ||
143 | "impl From <Leaf > for TokenTree {fn from (it : Leaf) -> TokenTree {TokenTree ::Leaf (it)}} \ | ||
144 | impl From <Subtree > for TokenTree {fn from (it : Subtree) -> TokenTree {TokenTree ::Subtree (it)}}" | ||
145 | ); | ||
146 | } | ||
147 | |||
148 | #[test] | ||
149 | fn test_convert_tt2() { | ||
150 | parse_macro( | ||
151 | r#" | ||
152 | macro_rules! impl_froms { | ||
153 | ($e:ident: $($v:ident),*) => { | ||
154 | $( | ||
155 | impl From<$v> for $e { | ||
156 | fn from(it: $v) -> $e { | ||
157 | $e::$v(it) | ||
158 | } | ||
159 | } | ||
160 | )* | ||
161 | } | ||
162 | } | ||
163 | "#, | ||
164 | ) | ||
165 | .assert_expand( | ||
166 | "impl_froms!(TokenTree: Leaf, Subtree);", | ||
167 | r#" | ||
168 | SUBTREE $ | ||
169 | IDENT impl 20 | ||
170 | IDENT From 21 | ||
171 | PUNCH < [joint] 22 | ||
172 | IDENT Leaf 53 | ||
173 | PUNCH > [alone] 25 | ||
174 | IDENT for 26 | ||
175 | IDENT TokenTree 51 | ||
176 | SUBTREE {} 29 | ||
177 | IDENT fn 30 | ||
178 | IDENT from 31 | ||
179 | SUBTREE () 32 | ||
180 | IDENT it 33 | ||
181 | PUNCH : [alone] 34 | ||
182 | IDENT Leaf 53 | ||
183 | PUNCH - [joint] 37 | ||
184 | PUNCH > [alone] 38 | ||
185 | IDENT TokenTree 51 | ||
186 | SUBTREE {} 41 | ||
187 | IDENT TokenTree 51 | ||
188 | PUNCH : [joint] 44 | ||
189 | PUNCH : [joint] 45 | ||
190 | IDENT Leaf 53 | ||
191 | SUBTREE () 48 | ||
192 | IDENT it 49 | ||
193 | IDENT impl 20 | ||
194 | IDENT From 21 | ||
195 | PUNCH < [joint] 22 | ||
196 | IDENT Subtree 55 | ||
197 | PUNCH > [alone] 25 | ||
198 | IDENT for 26 | ||
199 | IDENT TokenTree 51 | ||
200 | SUBTREE {} 29 | ||
201 | IDENT fn 30 | ||
202 | IDENT from 31 | ||
203 | SUBTREE () 32 | ||
204 | IDENT it 33 | ||
205 | PUNCH : [alone] 34 | ||
206 | IDENT Subtree 55 | ||
207 | PUNCH - [joint] 37 | ||
208 | PUNCH > [alone] 38 | ||
209 | IDENT TokenTree 51 | ||
210 | SUBTREE {} 41 | ||
211 | IDENT TokenTree 51 | ||
212 | PUNCH : [joint] 44 | ||
213 | PUNCH : [joint] 45 | ||
214 | IDENT Subtree 55 | ||
215 | SUBTREE () 48 | ||
216 | IDENT it 49 | ||
217 | "#, | ||
218 | ); | ||
219 | } | ||
220 | |||
221 | #[test] | ||
222 | fn test_lifetime_split() { | ||
223 | parse_macro( | ||
224 | r#" | ||
225 | macro_rules! foo { | ||
226 | ($($t:tt)*) => { $($t)*} | ||
227 | } | ||
228 | "#, | ||
229 | ) | ||
230 | .assert_expand( | ||
231 | r#"foo!(static bar: &'static str = "hello";);"#, | ||
232 | r#" | ||
233 | SUBTREE $ | ||
234 | IDENT static 17 | ||
235 | IDENT bar 18 | ||
236 | PUNCH : [alone] 19 | ||
237 | PUNCH & [alone] 20 | ||
238 | PUNCH ' [joint] 21 | ||
239 | IDENT static 22 | ||
240 | IDENT str 23 | ||
241 | PUNCH = [alone] 24 | ||
242 | LITERAL "hello" 25 | ||
243 | PUNCH ; [joint] 26 | ||
244 | "#, | ||
245 | ); | ||
246 | } | ||
247 | |||
248 | #[test] | ||
249 | fn test_expr_order() { | ||
250 | let expanded = parse_macro( | ||
251 | r#" | ||
252 | macro_rules! foo { | ||
253 | ($ i:expr) => { | ||
254 | fn bar() { $ i * 2; } | ||
255 | } | ||
256 | } | ||
257 | "#, | ||
258 | ) | ||
259 | .expand_items("foo! { 1 + 1}"); | ||
260 | |||
261 | let dump = format!("{:#?}", expanded); | ||
262 | assert_eq_text!( | ||
263 | r#"[email protected] | ||
264 | [email protected] | ||
265 | [email protected] "fn" | ||
266 | [email protected] | ||
267 | [email protected] "bar" | ||
268 | [email protected] | ||
269 | [email protected] "(" | ||
270 | [email protected] ")" | ||
271 | [email protected] | ||
272 | [email protected] "{" | ||
273 | [email protected] | ||
274 | [email protected] | ||
275 | [email protected] | ||
276 | [email protected] | ||
277 | [email protected] "1" | ||
278 | [email protected] "+" | ||
279 | [email protected] | ||
280 | [email protected] "1" | ||
281 | [email protected] "*" | ||
282 | [email protected] | ||
283 | [email protected] "2" | ||
284 | [email protected] ";" | ||
285 | [email protected] "}""#, | ||
286 | dump.trim() | ||
287 | ); | ||
288 | } | ||
289 | |||
290 | #[test] | ||
291 | fn test_fail_match_pattern_by_first_token() { | ||
292 | parse_macro( | ||
293 | r#" | ||
294 | macro_rules! foo { | ||
295 | ($ i:ident) => ( | ||
296 | mod $ i {} | ||
297 | ); | ||
298 | (= $ i:ident) => ( | ||
299 | fn $ i() {} | ||
300 | ); | ||
301 | (+ $ i:ident) => ( | ||
302 | struct $ i; | ||
303 | ) | ||
304 | } | ||
305 | "#, | ||
306 | ) | ||
307 | .assert_expand_items("foo! { foo }", "mod foo {}") | ||
308 | .assert_expand_items("foo! { = bar }", "fn bar () {}") | ||
309 | .assert_expand_items("foo! { + Baz }", "struct Baz ;"); | ||
310 | } | ||
311 | |||
312 | #[test] | ||
313 | fn test_fail_match_pattern_by_last_token() { | ||
314 | parse_macro( | ||
315 | r#" | ||
316 | macro_rules! foo { | ||
317 | ($ i:ident) => ( | ||
318 | mod $ i {} | ||
319 | ); | ||
320 | ($ i:ident =) => ( | ||
321 | fn $ i() {} | ||
322 | ); | ||
323 | ($ i:ident +) => ( | ||
324 | struct $ i; | ||
325 | ) | ||
326 | } | ||
327 | "#, | ||
328 | ) | ||
329 | .assert_expand_items("foo! { foo }", "mod foo {}") | ||
330 | .assert_expand_items("foo! { bar = }", "fn bar () {}") | ||
331 | .assert_expand_items("foo! { Baz + }", "struct Baz ;"); | ||
332 | } | ||
333 | |||
334 | #[test] | ||
335 | fn test_fail_match_pattern_by_word_token() { | ||
336 | parse_macro( | ||
337 | r#" | ||
338 | macro_rules! foo { | ||
339 | ($ i:ident) => ( | ||
340 | mod $ i {} | ||
341 | ); | ||
342 | (spam $ i:ident) => ( | ||
343 | fn $ i() {} | ||
344 | ); | ||
345 | (eggs $ i:ident) => ( | ||
346 | struct $ i; | ||
347 | ) | ||
348 | } | ||
349 | "#, | ||
350 | ) | ||
351 | .assert_expand_items("foo! { foo }", "mod foo {}") | ||
352 | .assert_expand_items("foo! { spam bar }", "fn bar () {}") | ||
353 | .assert_expand_items("foo! { eggs Baz }", "struct Baz ;"); | ||
354 | } | ||
355 | |||
356 | #[test] | ||
357 | fn test_match_group_pattern_by_separator_token() { | ||
358 | parse_macro( | ||
359 | r#" | ||
360 | macro_rules! foo { | ||
361 | ($ ($ i:ident),*) => ($ ( | ||
362 | mod $ i {} | ||
363 | )*); | ||
364 | ($ ($ i:ident)#*) => ($ ( | ||
365 | fn $ i() {} | ||
366 | )*); | ||
367 | ($ i:ident ,# $ j:ident) => ( | ||
368 | struct $ i; | ||
369 | struct $ j; | ||
370 | ) | ||
371 | } | ||
372 | "#, | ||
373 | ) | ||
374 | .assert_expand_items("foo! { foo, bar }", "mod foo {} mod bar {}") | ||
375 | .assert_expand_items("foo! { foo# bar }", "fn foo () {} fn bar () {}") | ||
376 | .assert_expand_items("foo! { Foo,# Bar }", "struct Foo ; struct Bar ;"); | ||
377 | } | ||
378 | |||
379 | #[test] | ||
380 | fn test_match_group_pattern_with_multiple_defs() { | ||
381 | parse_macro( | ||
382 | r#" | ||
383 | macro_rules! foo { | ||
384 | ($ ($ i:ident),*) => ( struct Bar { $ ( | ||
385 | fn $ i {} | ||
386 | )*} ); | ||
387 | } | ||
388 | "#, | ||
389 | ) | ||
390 | .assert_expand_items("foo! { foo, bar }", "struct Bar {fn foo {} fn bar {}}"); | ||
391 | } | ||
392 | |||
393 | #[test] | ||
394 | fn test_match_group_pattern_with_multiple_statement() { | ||
395 | parse_macro( | ||
396 | r#" | ||
397 | macro_rules! foo { | ||
398 | ($ ($ i:ident),*) => ( fn baz { $ ( | ||
399 | $ i (); | ||
400 | )*} ); | ||
401 | } | ||
402 | "#, | ||
403 | ) | ||
404 | .assert_expand_items("foo! { foo, bar }", "fn baz {foo () ; bar () ;}"); | ||
405 | } | ||
406 | |||
407 | #[test] | ||
408 | fn test_match_group_pattern_with_multiple_statement_without_semi() { | ||
409 | parse_macro( | ||
410 | r#" | ||
411 | macro_rules! foo { | ||
412 | ($ ($ i:ident),*) => ( fn baz { $ ( | ||
413 | $i() | ||
414 | );*} ); | ||
415 | } | ||
416 | "#, | ||
417 | ) | ||
418 | .assert_expand_items("foo! { foo, bar }", "fn baz {foo () ;bar ()}"); | ||
419 | } | ||
420 | |||
421 | #[test] | ||
422 | fn test_match_group_empty_fixed_token() { | ||
423 | parse_macro( | ||
424 | r#" | ||
425 | macro_rules! foo { | ||
426 | ($ ($ i:ident)* #abc) => ( fn baz { $ ( | ||
427 | $ i (); | ||
428 | )*} ); | ||
429 | } | ||
430 | "#, | ||
431 | ) | ||
432 | .assert_expand_items("foo! {#abc}", "fn baz {}"); | ||
433 | } | ||
434 | |||
435 | #[test] | ||
436 | fn test_match_group_in_subtree() { | ||
437 | parse_macro( | ||
438 | r#" | ||
439 | macro_rules! foo { | ||
440 | (fn $name:ident {$($i:ident)*} ) => ( fn $name() { $ ( | ||
441 | $ i (); | ||
442 | )*} ); | ||
443 | }"#, | ||
444 | ) | ||
445 | .assert_expand_items("foo! {fn baz {a b} }", "fn baz () {a () ; b () ;}"); | ||
446 | } | ||
447 | |||
448 | #[test] | ||
449 | fn test_match_group_with_multichar_sep() { | ||
450 | parse_macro( | ||
451 | r#" | ||
452 | macro_rules! foo { | ||
453 | (fn $name:ident {$($i:literal)*} ) => ( fn $name() -> bool { $($i)&&*} ); | ||
454 | }"#, | ||
455 | ) | ||
456 | .assert_expand_items("foo! (fn baz {true true} );", "fn baz () -> bool {true &&true}"); | ||
457 | } | ||
458 | |||
459 | #[test] | ||
460 | fn test_match_group_with_multichar_sep2() { | ||
461 | parse_macro( | ||
462 | r#" | ||
463 | macro_rules! foo { | ||
464 | (fn $name:ident {$($i:literal)&&*} ) => ( fn $name() -> bool { $($i)&&*} ); | ||
465 | }"#, | ||
466 | ) | ||
467 | .assert_expand_items("foo! (fn baz {true && true} );", "fn baz () -> bool {true &&true}"); | ||
468 | } | ||
469 | |||
470 | #[test] | ||
471 | fn test_match_group_zero_match() { | ||
472 | parse_macro( | ||
473 | r#" | ||
474 | macro_rules! foo { | ||
475 | ( $($i:ident)* ) => (); | ||
476 | }"#, | ||
477 | ) | ||
478 | .assert_expand_items("foo! ();", ""); | ||
479 | } | ||
480 | |||
481 | #[test] | ||
482 | fn test_match_group_in_group() { | ||
483 | parse_macro( | ||
484 | r#" | ||
485 | macro_rules! foo { | ||
486 | { $( ( $($i:ident)* ) )* } => ( $( ( $($i)* ) )* ); | ||
487 | }"#, | ||
488 | ) | ||
489 | .assert_expand_items("foo! ( (a b) );", "(a b)"); | ||
490 | } | ||
491 | |||
492 | #[test] | ||
493 | fn test_expand_to_item_list() { | ||
494 | let tree = parse_macro( | ||
495 | " | ||
496 | macro_rules! structs { | ||
497 | ($($i:ident),*) => { | ||
498 | $(struct $i { field: u32 } )* | ||
499 | } | ||
500 | } | ||
501 | ", | ||
502 | ) | ||
503 | .expand_items("structs!(Foo, Bar);"); | ||
504 | assert_eq!( | ||
505 | format!("{:#?}", tree).trim(), | ||
506 | r#" | ||
507 | [email protected] | ||
508 | [email protected] | ||
509 | [email protected] "struct" | ||
510 | [email protected] | ||
511 | [email protected] "Foo" | ||
512 | [email protected] | ||
513 | [email protected] "{" | ||
514 | [email protected] | ||
515 | [email protected] | ||
516 | [email protected] "field" | ||
517 | [email protected] ":" | ||
518 | [email protected] | ||
519 | [email protected] | ||
520 | [email protected] | ||
521 | [email protected] | ||
522 | [email protected] "u32" | ||
523 | [email protected] "}" | ||
524 | [email protected] | ||
525 | [email protected] "struct" | ||
526 | [email protected] | ||
527 | [email protected] "Bar" | ||
528 | [email protected] | ||
529 | [email protected] "{" | ||
530 | [email protected] | ||
531 | [email protected] | ||
532 | [email protected] "field" | ||
533 | [email protected] ":" | ||
534 | [email protected] | ||
535 | [email protected] | ||
536 | [email protected] | ||
537 | [email protected] | ||
538 | [email protected] "u32" | ||
539 | [email protected] "}""# | ||
540 | .trim() | ||
541 | ); | ||
542 | } | ||
543 | |||
544 | fn to_subtree(tt: &tt::TokenTree) -> &tt::Subtree { | ||
545 | if let tt::TokenTree::Subtree(subtree) = tt { | ||
546 | return &subtree; | ||
547 | } | ||
548 | unreachable!("It is not a subtree"); | ||
549 | } | ||
550 | fn to_literal(tt: &tt::TokenTree) -> &tt::Literal { | ||
551 | if let tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) = tt { | ||
552 | return lit; | ||
553 | } | ||
554 | unreachable!("It is not a literal"); | ||
555 | } | ||
556 | |||
557 | fn to_punct(tt: &tt::TokenTree) -> &tt::Punct { | ||
558 | if let tt::TokenTree::Leaf(tt::Leaf::Punct(lit)) = tt { | ||
559 | return lit; | ||
560 | } | ||
561 | unreachable!("It is not a Punct"); | ||
562 | } | ||
563 | |||
564 | #[test] | ||
565 | fn test_expand_literals_to_token_tree() { | ||
566 | let expansion = parse_macro( | ||
567 | r#" | ||
568 | macro_rules! literals { | ||
569 | ($i:ident) => { | ||
570 | { | ||
571 | let a = 'c'; | ||
572 | let c = 1000; | ||
573 | let f = 12E+99_f64; | ||
574 | let s = "rust1"; | ||
575 | } | ||
576 | } | ||
577 | } | ||
578 | "#, | ||
579 | ) | ||
580 | .expand_tt("literals!(foo);"); | ||
581 | let stm_tokens = &to_subtree(&expansion.token_trees[0]).token_trees; | ||
582 | |||
583 | // [let] [a] [=] ['c'] [;] | ||
584 | assert_eq!(to_literal(&stm_tokens[3]).text, "'c'"); | ||
585 | // [let] [c] [=] [1000] [;] | ||
586 | assert_eq!(to_literal(&stm_tokens[5 + 3]).text, "1000"); | ||
587 | // [let] [f] [=] [12E+99_f64] [;] | ||
588 | assert_eq!(to_literal(&stm_tokens[10 + 3]).text, "12E+99_f64"); | ||
589 | // [let] [s] [=] ["rust1"] [;] | ||
590 | assert_eq!(to_literal(&stm_tokens[15 + 3]).text, "\"rust1\""); | ||
591 | } | ||
592 | |||
593 | #[test] | ||
594 | fn test_attr_to_token_tree() { | ||
595 | let expansion = parse_to_token_tree_by_syntax( | ||
596 | r#" | ||
597 | #[derive(Copy)] | ||
598 | struct Foo; | ||
599 | "#, | ||
600 | ); | ||
601 | |||
602 | assert_eq!(to_punct(&expansion.token_trees[0]).char, '#'); | ||
603 | assert_eq!( | ||
604 | to_subtree(&expansion.token_trees[1]).delimiter_kind(), | ||
605 | Some(tt::DelimiterKind::Bracket) | ||
606 | ); | ||
607 | } | ||
608 | |||
609 | #[test] | ||
610 | fn test_two_idents() { | ||
611 | parse_macro( | ||
612 | r#" | ||
613 | macro_rules! foo { | ||
614 | ($ i:ident, $ j:ident) => { | ||
615 | fn foo() { let a = $ i; let b = $j; } | ||
616 | } | ||
617 | } | ||
618 | "#, | ||
619 | ) | ||
620 | .assert_expand_items("foo! { foo, bar }", "fn foo () {let a = foo ; let b = bar ;}"); | ||
621 | } | ||
622 | |||
623 | #[test] | ||
624 | fn test_tt_to_stmts() { | ||
625 | let stmts = parse_macro( | ||
626 | r#" | ||
627 | macro_rules! foo { | ||
628 | () => { | ||
629 | let a = 0; | ||
630 | a = 10 + 1; | ||
631 | a | ||
632 | } | ||
633 | } | ||
634 | "#, | ||
635 | ) | ||
636 | .expand_statements("foo!{}"); | ||
637 | |||
638 | assert_eq!( | ||
639 | format!("{:#?}", stmts).trim(), | ||
640 | r#"[email protected] | ||
641 | [email protected] | ||
642 | [email protected] "let" | ||
643 | [email protected] | ||
644 | [email protected] | ||
645 | [email protected] "a" | ||
646 | [email protected] "=" | ||
647 | [email protected] | ||
648 | [email protected] "0" | ||
649 | [email protected] ";" | ||
650 | [email protected] | ||
651 | [email protected] | ||
652 | [email protected] | ||
653 | [email protected] | ||
654 | [email protected] | ||
655 | [email protected] | ||
656 | [email protected] "a" | ||
657 | [email protected] "=" | ||
658 | [email protected] | ||
659 | [email protected] | ||
660 | [email protected] "10" | ||
661 | [email protected] "+" | ||
662 | [email protected] | ||
663 | [email protected] "1" | ||
664 | [email protected] ";" | ||
665 | [email protected] | ||
666 | [email protected] | ||
667 | [email protected] | ||
668 | [email protected] | ||
669 | [email protected] "a""#, | ||
670 | ); | ||
671 | } | ||
672 | |||
673 | #[test] | ||
674 | fn test_match_literal() { | ||
675 | parse_macro( | ||
676 | r#" | ||
677 | macro_rules! foo { | ||
678 | ('(') => { | ||
679 | fn foo() {} | ||
680 | } | ||
681 | } | ||
682 | "#, | ||
683 | ) | ||
684 | .assert_expand_items("foo! ['('];", "fn foo () {}"); | ||
685 | } | ||
686 | |||
687 | #[test] | ||
688 | fn test_parse_macro_def_simple() { | ||
689 | cov_mark::check!(parse_macro_def_simple); | ||
690 | |||
691 | parse_macro2( | ||
692 | r#" | ||
693 | macro foo($id:ident) { | ||
694 | fn $id() {} | ||
695 | } | ||
696 | "#, | ||
697 | ) | ||
698 | .assert_expand_items("foo!(bar);", "fn bar () {}"); | ||
699 | } | ||
700 | |||
701 | #[test] | ||
702 | fn test_parse_macro_def_rules() { | ||
703 | cov_mark::check!(parse_macro_def_rules); | ||
704 | |||
705 | parse_macro2( | ||
706 | r#" | ||
707 | macro foo { | ||
708 | ($id:ident) => { | ||
709 | fn $id() {} | ||
710 | } | ||
711 | } | ||
712 | "#, | ||
713 | ) | ||
714 | .assert_expand_items("foo!(bar);", "fn bar () {}"); | ||
715 | } | ||
716 | |||
717 | // The following tests are port from intellij-rust directly | ||
718 | // https://github.com/intellij-rust/intellij-rust/blob/c4e9feee4ad46e7953b1948c112533360b6087bb/src/test/kotlin/org/rust/lang/core/macros/RsMacroExpansionTest.kt | ||
719 | |||
720 | #[test] | ||
721 | fn test_path() { | ||
722 | parse_macro( | ||
723 | r#" | ||
724 | macro_rules! foo { | ||
725 | ($ i:path) => { | ||
726 | fn foo() { let a = $ i; } | ||
727 | } | ||
728 | } | ||
729 | "#, | ||
730 | ) | ||
731 | .assert_expand_items("foo! { foo }", "fn foo () {let a = foo ;}") | ||
732 | .assert_expand_items( | ||
733 | "foo! { bar::<u8>::baz::<u8> }", | ||
734 | "fn foo () {let a = bar ::< u8 >:: baz ::< u8 > ;}", | ||
735 | ); | ||
736 | } | ||
737 | |||
738 | #[test] | ||
739 | fn test_two_paths() { | ||
740 | parse_macro( | ||
741 | r#" | ||
742 | macro_rules! foo { | ||
743 | ($ i:path, $ j:path) => { | ||
744 | fn foo() { let a = $ i; let b = $j; } | ||
745 | } | ||
746 | } | ||
747 | "#, | ||
748 | ) | ||
749 | .assert_expand_items("foo! { foo, bar }", "fn foo () {let a = foo ; let b = bar ;}"); | ||
750 | } | ||
751 | |||
752 | #[test] | ||
753 | fn test_path_with_path() { | ||
754 | parse_macro( | ||
755 | r#" | ||
756 | macro_rules! foo { | ||
757 | ($ i:path) => { | ||
758 | fn foo() { let a = $ i :: bar; } | ||
759 | } | ||
760 | } | ||
761 | "#, | ||
762 | ) | ||
763 | .assert_expand_items("foo! { foo }", "fn foo () {let a = foo :: bar ;}"); | ||
764 | } | ||
765 | |||
766 | #[test] | ||
767 | fn test_expr() { | ||
768 | parse_macro( | ||
769 | r#" | ||
770 | macro_rules! foo { | ||
771 | ($ i:expr) => { | ||
772 | fn bar() { $ i; } | ||
773 | } | ||
774 | } | ||
775 | "#, | ||
776 | ) | ||
777 | .assert_expand_items( | ||
778 | "foo! { 2 + 2 * baz(3).quux() }", | ||
779 | "fn bar () {2 + 2 * baz (3) . quux () ;}", | ||
780 | ); | ||
781 | } | ||
782 | |||
783 | #[test] | ||
784 | fn test_last_expr() { | ||
785 | parse_macro( | ||
786 | r#" | ||
787 | macro_rules! vec { | ||
788 | ($($item:expr),*) => { | ||
789 | { | ||
790 | let mut v = Vec::new(); | ||
791 | $( | ||
792 | v.push($item); | ||
793 | )* | ||
794 | v | ||
795 | } | ||
796 | }; | ||
797 | } | ||
798 | "#, | ||
799 | ) | ||
800 | .assert_expand_items( | ||
801 | "vec!(1,2,3);", | ||
802 | "{let mut v = Vec :: new () ; v . push (1) ; v . push (2) ; v . push (3) ; v}", | ||
803 | ); | ||
804 | } | ||
805 | |||
806 | #[test] | ||
807 | fn test_expr_with_attr() { | ||
808 | parse_macro( | ||
809 | r#" | ||
810 | macro_rules! m { | ||
811 | ($a:expr) => {0} | ||
812 | } | ||
813 | "#, | ||
814 | ) | ||
815 | .assert_expand_items("m!(#[allow(a)]())", "0"); | ||
816 | } | ||
817 | |||
818 | #[test] | ||
819 | fn test_ty() { | ||
820 | parse_macro( | ||
821 | r#" | ||
822 | macro_rules! foo { | ||
823 | ($ i:ty) => ( | ||
824 | fn bar() -> $ i { unimplemented!() } | ||
825 | ) | ||
826 | } | ||
827 | "#, | ||
828 | ) | ||
829 | .assert_expand_items("foo! { Baz<u8> }", "fn bar () -> Baz < u8 > {unimplemented ! ()}"); | ||
830 | } | ||
831 | |||
832 | #[test] | ||
833 | fn test_ty_with_complex_type() { | ||
834 | parse_macro( | ||
835 | r#" | ||
836 | macro_rules! foo { | ||
837 | ($ i:ty) => ( | ||
838 | fn bar() -> $ i { unimplemented!() } | ||
839 | ) | ||
840 | } | ||
841 | "#, | ||
842 | ) | ||
843 | // Reference lifetime struct with generic type | ||
844 | .assert_expand_items( | ||
845 | "foo! { &'a Baz<u8> }", | ||
846 | "fn bar () -> & 'a Baz < u8 > {unimplemented ! ()}", | ||
847 | ) | ||
848 | // extern "Rust" func type | ||
849 | .assert_expand_items( | ||
850 | r#"foo! { extern "Rust" fn() -> Ret }"#, | ||
851 | r#"fn bar () -> extern "Rust" fn () -> Ret {unimplemented ! ()}"#, | ||
852 | ); | ||
853 | } | ||
854 | |||
855 | #[test] | ||
856 | fn test_pat_() { | ||
857 | parse_macro( | ||
858 | r#" | ||
859 | macro_rules! foo { | ||
860 | ($ i:pat) => { fn foo() { let $ i; } } | ||
861 | } | ||
862 | "#, | ||
863 | ) | ||
864 | .assert_expand_items("foo! { (a, b) }", "fn foo () {let (a , b) ;}"); | ||
865 | } | ||
866 | |||
867 | #[test] | ||
868 | fn test_stmt() { | ||
869 | parse_macro( | ||
870 | r#" | ||
871 | macro_rules! foo { | ||
872 | ($ i:stmt) => ( | ||
873 | fn bar() { $ i; } | ||
874 | ) | ||
875 | } | ||
876 | "#, | ||
877 | ) | ||
878 | .assert_expand_items("foo! { 2 }", "fn bar () {2 ;}") | ||
879 | .assert_expand_items("foo! { let a = 0 }", "fn bar () {let a = 0 ;}"); | ||
880 | } | ||
881 | |||
882 | #[test] | ||
883 | fn test_single_item() { | ||
884 | parse_macro( | ||
885 | r#" | ||
886 | macro_rules! foo { | ||
887 | ($ i:item) => ( | ||
888 | $ i | ||
889 | ) | ||
890 | } | ||
891 | "#, | ||
892 | ) | ||
893 | .assert_expand_items("foo! {mod c {}}", "mod c {}"); | ||
894 | } | ||
895 | |||
896 | #[test] | ||
897 | fn test_all_items() { | ||
898 | parse_macro( | ||
899 | r#" | ||
900 | macro_rules! foo { | ||
901 | ($ ($ i:item)*) => ($ ( | ||
902 | $ i | ||
903 | )*) | ||
904 | } | ||
905 | "#, | ||
906 | ). | ||
907 | assert_expand_items( | ||
908 | r#" | ||
909 | foo! { | ||
910 | extern crate a; | ||
911 | mod b; | ||
912 | mod c {} | ||
913 | use d; | ||
914 | const E: i32 = 0; | ||
915 | static F: i32 = 0; | ||
916 | impl G {} | ||
917 | struct H; | ||
918 | enum I { Foo } | ||
919 | trait J {} | ||
920 | fn h() {} | ||
921 | extern {} | ||
922 | type T = u8; | ||
923 | } | ||
924 | "#, | ||
925 | r#"extern crate a ; mod b ; mod c {} use d ; const E : i32 = 0 ; static F : i32 = 0 ; impl G {} struct H ; enum I {Foo} trait J {} fn h () {} extern {} type T = u8 ;"#, | ||
926 | ); | ||
927 | } | ||
928 | |||
929 | #[test] | ||
930 | fn test_block() { | ||
931 | parse_macro( | ||
932 | r#" | ||
933 | macro_rules! foo { | ||
934 | ($ i:block) => { fn foo() $ i } | ||
935 | } | ||
936 | "#, | ||
937 | ) | ||
938 | .assert_expand_statements("foo! { { 1; } }", "fn foo () {1 ;}"); | ||
939 | } | ||
940 | |||
941 | #[test] | ||
942 | fn test_meta() { | ||
943 | parse_macro( | ||
944 | r#" | ||
945 | macro_rules! foo { | ||
946 | ($ i:meta) => ( | ||
947 | #[$ i] | ||
948 | fn bar() {} | ||
949 | ) | ||
950 | } | ||
951 | "#, | ||
952 | ) | ||
953 | .assert_expand_items( | ||
954 | r#"foo! { cfg(target_os = "windows") }"#, | ||
955 | r#"# [cfg (target_os = "windows")] fn bar () {}"#, | ||
956 | ) | ||
957 | .assert_expand_items(r#"foo! { hello::world }"#, r#"# [hello :: world] fn bar () {}"#); | ||
958 | } | ||
959 | |||
960 | #[test] | ||
961 | fn test_meta_doc_comments() { | ||
962 | parse_macro( | ||
963 | r#" | ||
964 | macro_rules! foo { | ||
965 | ($(#[$ i:meta])+) => ( | ||
966 | $(#[$ i])+ | ||
967 | fn bar() {} | ||
968 | ) | ||
969 | } | ||
970 | "#, | ||
971 | ). | ||
972 | assert_expand_items( | ||
973 | r#"foo! { | ||
974 | /// Single Line Doc 1 | ||
975 | /** | ||
976 | MultiLines Doc | ||
977 | */ | ||
978 | }"#, | ||
979 | "# [doc = \" Single Line Doc 1\"] # [doc = \"\\\\n MultiLines Doc\\\\n \"] fn bar () {}", | ||
980 | ); | ||
981 | } | ||
982 | |||
983 | #[test] | ||
984 | fn test_meta_doc_comments_non_latin() { | ||
985 | parse_macro( | ||
986 | r#" | ||
987 | macro_rules! foo { | ||
988 | ($(#[$ i:meta])+) => ( | ||
989 | $(#[$ i])+ | ||
990 | fn bar() {} | ||
991 | ) | ||
992 | } | ||
993 | "#, | ||
994 | ). | ||
995 | assert_expand_items( | ||
996 | r#"foo! { | ||
997 | /// 錦瑟無端五十弦,一弦一柱思華年。 | ||
998 | /** | ||
999 | 莊生曉夢迷蝴蝶,望帝春心託杜鵑。 | ||
1000 | */ | ||
1001 | }"#, | ||
1002 | "# [doc = \" 錦瑟無端五十弦,一弦一柱思華年。\"] # [doc = \"\\\\n 莊生曉夢迷蝴蝶,望帝春心託杜鵑。\\\\n \"] fn bar () {}", | ||
1003 | ); | ||
1004 | } | ||
1005 | |||
1006 | #[test] | ||
1007 | fn test_tt_block() { | ||
1008 | parse_macro( | ||
1009 | r#" | ||
1010 | macro_rules! foo { | ||
1011 | ($ i:tt) => { fn foo() $ i } | ||
1012 | } | ||
1013 | "#, | ||
1014 | ) | ||
1015 | .assert_expand_items(r#"foo! { { 1; } }"#, r#"fn foo () {1 ;}"#); | ||
1016 | } | ||
1017 | |||
1018 | #[test] | ||
1019 | fn test_tt_group() { | ||
1020 | parse_macro( | ||
1021 | r#" | ||
1022 | macro_rules! foo { | ||
1023 | ($($ i:tt)*) => { $($ i)* } | ||
1024 | } | ||
1025 | "#, | ||
1026 | ) | ||
1027 | .assert_expand_items(r#"foo! { fn foo() {} }"#, r#"fn foo () {}"#); | ||
1028 | } | ||
1029 | |||
1030 | #[test] | ||
1031 | fn test_tt_composite() { | ||
1032 | parse_macro( | ||
1033 | r#" | ||
1034 | macro_rules! foo { | ||
1035 | ($i:tt) => { 0 } | ||
1036 | } | ||
1037 | "#, | ||
1038 | ) | ||
1039 | .assert_expand_items(r#"foo! { => }"#, r#"0"#); | ||
1040 | } | ||
1041 | |||
1042 | #[test] | ||
1043 | fn test_tt_composite2() { | ||
1044 | let node = parse_macro( | ||
1045 | r#" | ||
1046 | macro_rules! foo { | ||
1047 | ($($tt:tt)*) => { abs!(=> $($tt)*) } | ||
1048 | } | ||
1049 | "#, | ||
1050 | ) | ||
1051 | .expand_items(r#"foo!{#}"#); | ||
1052 | |||
1053 | let res = format!("{:#?}", &node); | ||
1054 | assert_eq_text!( | ||
1055 | r###"[email protected] | ||
1056 | [email protected] | ||
1057 | [email protected] | ||
1058 | [email protected] | ||
1059 | [email protected] | ||
1060 | [email protected] "abs" | ||
1061 | [email protected] "!" | ||
1062 | [email protected] | ||
1063 | [email protected] "(" | ||
1064 | [email protected] "=" | ||
1065 | [email protected] ">" | ||
1066 | [email protected] " " | ||
1067 | [email protected] "#" | ||
1068 | [email protected] ")""###, | ||
1069 | res.trim() | ||
1070 | ); | ||
1071 | } | ||
1072 | |||
1073 | #[test] | ||
1074 | fn test_tt_with_composite_without_space() { | ||
1075 | parse_macro( | ||
1076 | r#" | ||
1077 | macro_rules! foo { | ||
1078 | ($ op:tt, $j:path) => ( | ||
1079 | 0 | ||
1080 | ) | ||
1081 | } | ||
1082 | "#, | ||
1083 | ) | ||
1084 | // Test macro input without any spaces | ||
1085 | // See https://github.com/rust-analyzer/rust-analyzer/issues/6692 | ||
1086 | .assert_expand_items("foo!(==,Foo::Bool)", "0"); | ||
1087 | } | ||
1088 | |||
1089 | #[test] | ||
1090 | fn test_underscore() { | ||
1091 | parse_macro( | ||
1092 | r#" | ||
1093 | macro_rules! foo { | ||
1094 | ($_:tt) => { 0 } | ||
1095 | } | ||
1096 | "#, | ||
1097 | ) | ||
1098 | .assert_expand_items(r#"foo! { => }"#, r#"0"#); | ||
1099 | } | ||
1100 | |||
1101 | #[test] | ||
1102 | fn test_underscore_not_greedily() { | ||
1103 | parse_macro( | ||
1104 | r#" | ||
1105 | macro_rules! q { | ||
1106 | ($($a:ident)* _) => {0}; | ||
1107 | } | ||
1108 | "#, | ||
1109 | ) | ||
1110 | // `_` overlaps with `$a:ident` but rustc matches it under the `_` token | ||
1111 | .assert_expand_items(r#"q![a b c d _]"#, r#"0"#); | ||
1112 | |||
1113 | parse_macro( | ||
1114 | r#" | ||
1115 | macro_rules! q { | ||
1116 | ($($a:expr => $b:ident)* _ => $c:expr) => {0}; | ||
1117 | } | ||
1118 | "#, | ||
1119 | ) | ||
1120 | // `_ => ou` overlaps with `$a:expr => $b:ident` but rustc matches it under `_ => $c:expr` | ||
1121 | .assert_expand_items(r#"q![a => b c => d _ => ou]"#, r#"0"#); | ||
1122 | } | ||
1123 | |||
1124 | #[test] | ||
1125 | fn test_underscore_as_type() { | ||
1126 | parse_macro( | ||
1127 | r#" | ||
1128 | macro_rules! q { | ||
1129 | ($a:ty) => {0}; | ||
1130 | } | ||
1131 | "#, | ||
1132 | ) | ||
1133 | // Underscore is a type | ||
1134 | .assert_expand_items(r#"q![_]"#, r#"0"#); | ||
1135 | } | ||
1136 | |||
1137 | #[test] | ||
1138 | fn test_vertical_bar_with_pat() { | ||
1139 | parse_macro( | ||
1140 | r#" | ||
1141 | macro_rules! foo { | ||
1142 | (| $pat:pat | ) => { 0 } | ||
1143 | } | ||
1144 | "#, | ||
1145 | ) | ||
1146 | .assert_expand_items(r#"foo! { | x | }"#, r#"0"#); | ||
1147 | } | ||
1148 | |||
1149 | #[test] | ||
1150 | fn test_dollar_crate_lhs_is_not_meta() { | ||
1151 | parse_macro( | ||
1152 | r#" | ||
1153 | macro_rules! foo { | ||
1154 | ($crate) => {}; | ||
1155 | () => {0}; | ||
1156 | } | ||
1157 | "#, | ||
1158 | ) | ||
1159 | .assert_expand_items(r#"foo!{}"#, r#"0"#); | ||
1160 | } | ||
1161 | |||
1162 | #[test] | ||
1163 | fn test_lifetime() { | ||
1164 | parse_macro( | ||
1165 | r#" | ||
1166 | macro_rules! foo { | ||
1167 | ($ lt:lifetime) => { struct Ref<$ lt>{ s: &$ lt str } } | ||
1168 | } | ||
1169 | "#, | ||
1170 | ) | ||
1171 | .assert_expand_items(r#"foo!{'a}"#, r#"struct Ref <'a > {s : &'a str}"#); | ||
1172 | } | ||
1173 | |||
1174 | #[test] | ||
1175 | fn test_literal() { | ||
1176 | parse_macro( | ||
1177 | r#" | ||
1178 | macro_rules! foo { | ||
1179 | ($ type:ty , $ lit:literal) => { const VALUE: $ type = $ lit;}; | ||
1180 | } | ||
1181 | "#, | ||
1182 | ) | ||
1183 | .assert_expand_items(r#"foo!(u8,0);"#, r#"const VALUE : u8 = 0 ;"#); | ||
1184 | |||
1185 | parse_macro( | ||
1186 | r#" | ||
1187 | macro_rules! foo { | ||
1188 | ($ type:ty , $ lit:literal) => { const VALUE: $ type = $ lit;}; | ||
1189 | } | ||
1190 | "#, | ||
1191 | ) | ||
1192 | .assert_expand_items(r#"foo!(i32,-1);"#, r#"const VALUE : i32 = - 1 ;"#); | ||
1193 | } | ||
1194 | |||
1195 | #[test] | ||
1196 | fn test_boolean_is_ident() { | ||
1197 | parse_macro( | ||
1198 | r#" | ||
1199 | macro_rules! foo { | ||
1200 | ($lit0:literal, $lit1:literal) => { const VALUE: (bool,bool) = ($lit0,$lit1); }; | ||
1201 | } | ||
1202 | "#, | ||
1203 | ) | ||
1204 | .assert_expand( | ||
1205 | r#"foo!(true,false);"#, | ||
1206 | r#" | ||
1207 | SUBTREE $ | ||
1208 | IDENT const 14 | ||
1209 | IDENT VALUE 15 | ||
1210 | PUNCH : [alone] 16 | ||
1211 | SUBTREE () 17 | ||
1212 | IDENT bool 18 | ||
1213 | PUNCH , [alone] 19 | ||
1214 | IDENT bool 20 | ||
1215 | PUNCH = [alone] 21 | ||
1216 | SUBTREE () 22 | ||
1217 | IDENT true 29 | ||
1218 | PUNCH , [joint] 25 | ||
1219 | IDENT false 31 | ||
1220 | PUNCH ; [alone] 28 | ||
1221 | "#, | ||
1222 | ); | ||
1223 | } | ||
1224 | |||
1225 | #[test] | ||
1226 | fn test_vis() { | ||
1227 | parse_macro( | ||
1228 | r#" | ||
1229 | macro_rules! foo { | ||
1230 | ($ vis:vis $ name:ident) => { $ vis fn $ name() {}}; | ||
1231 | } | ||
1232 | "#, | ||
1233 | ) | ||
1234 | .assert_expand_items(r#"foo!(pub foo);"#, r#"pub fn foo () {}"#) | ||
1235 | // test optional cases | ||
1236 | .assert_expand_items(r#"foo!(foo);"#, r#"fn foo () {}"#); | ||
1237 | } | ||
1238 | |||
1239 | #[test] | ||
1240 | fn test_inner_macro_rules() { | ||
1241 | parse_macro( | ||
1242 | r#" | ||
1243 | macro_rules! foo { | ||
1244 | ($a:ident, $b:ident, $c:tt) => { | ||
1245 | |||
1246 | macro_rules! bar { | ||
1247 | ($bi:ident) => { | ||
1248 | fn $bi() -> u8 {$c} | ||
1249 | } | ||
1250 | } | ||
1251 | |||
1252 | bar!($a); | ||
1253 | fn $b() -> u8 {$c} | ||
1254 | } | ||
1255 | } | ||
1256 | "#, | ||
1257 | ). | ||
1258 | assert_expand_items( | ||
1259 | r#"foo!(x,y, 1);"#, | ||
1260 | r#"macro_rules ! bar {($ bi : ident) => {fn $ bi () -> u8 {1}}} bar ! (x) ; fn y () -> u8 {1}"#, | ||
1261 | ); | ||
1262 | } | ||
1263 | |||
1264 | #[test] | ||
1265 | fn test_expr_after_path_colons() { | ||
1266 | assert!(parse_macro( | ||
1267 | r#" | ||
1268 | macro_rules! m { | ||
1269 | ($k:expr) => { | ||
1270 | f(K::$k); | ||
1271 | } | ||
1272 | } | ||
1273 | "#, | ||
1274 | ) | ||
1275 | .expand_statements(r#"m!(C("0"))"#) | ||
1276 | .descendants() | ||
1277 | .find(|token| token.kind() == ERROR) | ||
1278 | .is_some()); | ||
1279 | } | ||
1280 | |||
1281 | #[test] | ||
1282 | fn test_match_is_not_greedy() { | ||
1283 | parse_macro( | ||
1284 | r#" | ||
1285 | macro_rules! foo { | ||
1286 | ($($i:ident $(,)*),*) => {}; | ||
1287 | } | ||
1288 | "#, | ||
1289 | ) | ||
1290 | .assert_expand_items(r#"foo!(a,b);"#, r#""#); | ||
1291 | } | ||
1292 | |||
1293 | // The following tests are based on real world situations | ||
1294 | #[test] | ||
1295 | fn test_vec() { | ||
1296 | let fixture = parse_macro( | ||
1297 | r#" | ||
1298 | macro_rules! vec { | ||
1299 | ($($item:expr),*) => { | ||
1300 | { | ||
1301 | let mut v = Vec::new(); | ||
1302 | $( | ||
1303 | v.push($item); | ||
1304 | )* | ||
1305 | v | ||
1306 | } | ||
1307 | }; | ||
1308 | } | ||
1309 | "#, | ||
1310 | ); | ||
1311 | fixture | ||
1312 | .assert_expand_items(r#"vec!();"#, r#"{let mut v = Vec :: new () ; v}"#) | ||
1313 | .assert_expand_items( | ||
1314 | r#"vec![1u32,2];"#, | ||
1315 | r#"{let mut v = Vec :: new () ; v . push (1u32) ; v . push (2) ; v}"#, | ||
1316 | ); | ||
1317 | |||
1318 | let tree = fixture.expand_expr(r#"vec![1u32,2];"#); | ||
1319 | |||
1320 | assert_eq!( | ||
1321 | format!("{:#?}", tree).trim(), | ||
1322 | r#"[email protected] | ||
1323 | [email protected] "{" | ||
1324 | [email protected] | ||
1325 | [email protected] "let" | ||
1326 | [email protected] | ||
1327 | [email protected] "mut" | ||
1328 | [email protected] | ||
1329 | [email protected] "v" | ||
1330 | [email protected] "=" | ||
1331 | [email protected] | ||
1332 | [email protected] | ||
1333 | [email protected] | ||
1334 | [email protected] | ||
1335 | [email protected] | ||
1336 | [email protected] | ||
1337 | [email protected] "Vec" | ||
1338 | [email protected] "::" | ||
1339 | [email protected] | ||
1340 | [email protected] | ||
1341 | [email protected] "new" | ||
1342 | [email protected] | ||
1343 | [email protected] "(" | ||
1344 | [email protected] ")" | ||
1345 | [email protected] ";" | ||
1346 | [email protected] | ||
1347 | [email protected] | ||
1348 | [email protected] | ||
1349 | [email protected] | ||
1350 | [email protected] | ||
1351 | [email protected] | ||
1352 | [email protected] "v" | ||
1353 | [email protected] "." | ||
1354 | [email protected] | ||
1355 | [email protected] "push" | ||
1356 | [email protected] | ||
1357 | [email protected] "(" | ||
1358 | [email protected] | ||
1359 | [email protected] "1u32" | ||
1360 | [email protected] ")" | ||
1361 | [email protected] ";" | ||
1362 | [email protected] | ||
1363 | [email protected] | ||
1364 | [email protected] | ||
1365 | [email protected] | ||
1366 | [email protected] | ||
1367 | [email protected] | ||
1368 | [email protected] "v" | ||
1369 | [email protected] "." | ||
1370 | [email protected] | ||
1371 | [email protected] "push" | ||
1372 | [email protected] | ||
1373 | [email protected] "(" | ||
1374 | [email protected] | ||
1375 | [email protected] "2" | ||
1376 | [email protected] ")" | ||
1377 | [email protected] ";" | ||
1378 | [email protected] | ||
1379 | [email protected] | ||
1380 | [email protected] | ||
1381 | [email protected] | ||
1382 | [email protected] "v" | ||
1383 | [email protected] "}""# | ||
1384 | ); | ||
1385 | } | ||
1386 | |||
1387 | #[test] | ||
1388 | fn test_winapi_struct() { | ||
1389 | // from https://github.com/retep998/winapi-rs/blob/a7ef2bca086aae76cf6c4ce4c2552988ed9798ad/src/macros.rs#L366 | ||
1390 | |||
1391 | parse_macro( | ||
1392 | r#" | ||
1393 | macro_rules! STRUCT { | ||
1394 | ($(#[$attrs:meta])* struct $name:ident { | ||
1395 | $($field:ident: $ftype:ty,)+ | ||
1396 | }) => ( | ||
1397 | #[repr(C)] #[derive(Copy)] $(#[$attrs])* | ||
1398 | pub struct $name { | ||
1399 | $(pub $field: $ftype,)+ | ||
1400 | } | ||
1401 | impl Clone for $name { | ||
1402 | #[inline] | ||
1403 | fn clone(&self) -> $name { *self } | ||
1404 | } | ||
1405 | #[cfg(feature = "impl-default")] | ||
1406 | impl Default for $name { | ||
1407 | #[inline] | ||
1408 | fn default() -> $name { unsafe { $crate::_core::mem::zeroed() } } | ||
1409 | } | ||
1410 | ); | ||
1411 | } | ||
1412 | "#, | ||
1413 | ). | ||
1414 | // from https://github.com/retep998/winapi-rs/blob/a7ef2bca086aae76cf6c4ce4c2552988ed9798ad/src/shared/d3d9caps.rs | ||
1415 | assert_expand_items(r#"STRUCT!{struct D3DVSHADERCAPS2_0 {Caps: u8,}}"#, | ||
1416 | "# [repr (C)] # [derive (Copy)] pub struct D3DVSHADERCAPS2_0 {pub Caps : u8 ,} impl Clone for D3DVSHADERCAPS2_0 {# [inline] fn clone (& self) -> D3DVSHADERCAPS2_0 {* self}} # [cfg (feature = \"impl-default\")] impl Default for D3DVSHADERCAPS2_0 {# [inline] fn default () -> D3DVSHADERCAPS2_0 {unsafe {$crate :: _core :: mem :: zeroed ()}}}" | ||
1417 | ) | ||
1418 | .assert_expand_items(r#"STRUCT!{#[cfg_attr(target_arch = "x86", repr(packed))] struct D3DCONTENTPROTECTIONCAPS {Caps : u8 ,}}"#, | ||
1419 | "# [repr (C)] # [derive (Copy)] # [cfg_attr (target_arch = \"x86\" , repr (packed))] pub struct D3DCONTENTPROTECTIONCAPS {pub Caps : u8 ,} impl Clone for D3DCONTENTPROTECTIONCAPS {# [inline] fn clone (& self) -> D3DCONTENTPROTECTIONCAPS {* self}} # [cfg (feature = \"impl-default\")] impl Default for D3DCONTENTPROTECTIONCAPS {# [inline] fn default () -> D3DCONTENTPROTECTIONCAPS {unsafe {$crate :: _core :: mem :: zeroed ()}}}" | ||
1420 | ); | ||
1421 | } | ||
1422 | |||
1423 | #[test] | ||
1424 | fn test_int_base() { | ||
1425 | parse_macro( | ||
1426 | r#" | ||
1427 | macro_rules! int_base { | ||
1428 | ($Trait:ident for $T:ident as $U:ident -> $Radix:ident) => { | ||
1429 | #[stable(feature = "rust1", since = "1.0.0")] | ||
1430 | impl fmt::$Trait for $T { | ||
1431 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
1432 | $Radix.fmt_int(*self as $U, f) | ||
1433 | } | ||
1434 | } | ||
1435 | } | ||
1436 | } | ||
1437 | "#, | ||
1438 | ).assert_expand_items(r#" int_base!{Binary for isize as usize -> Binary}"#, | ||
1439 | "# [stable (feature = \"rust1\" , since = \"1.0.0\")] impl fmt ::Binary for isize {fn fmt (& self , f : & mut fmt :: Formatter < \'_ >) -> fmt :: Result {Binary . fmt_int (* self as usize , f)}}" | ||
1440 | ); | ||
1441 | } | ||
1442 | |||
1443 | #[test] | ||
1444 | fn test_generate_pattern_iterators() { | ||
1445 | // from https://github.com/rust-lang/rust/blob/316a391dcb7d66dc25f1f9a4ec9d368ef7615005/src/libcore/str/mod.rs | ||
1446 | parse_macro( | ||
1447 | r#" | ||
1448 | macro_rules! generate_pattern_iterators { | ||
1449 | { double ended; with $(#[$common_stability_attribute:meta])*, | ||
1450 | $forward_iterator:ident, | ||
1451 | $reverse_iterator:ident, $iterty:ty | ||
1452 | } => { | ||
1453 | fn foo(){} | ||
1454 | } | ||
1455 | } | ||
1456 | "#, | ||
1457 | ).assert_expand_items( | ||
1458 | r#"generate_pattern_iterators ! ( double ended ; with # [ stable ( feature = "rust1" , since = "1.0.0" ) ] , Split , RSplit , & 'a str );"#, | ||
1459 | "fn foo () {}", | ||
1460 | ); | ||
1461 | } | ||
1462 | |||
1463 | #[test] | ||
1464 | fn test_impl_fn_for_zst() { | ||
1465 | // from https://github.com/rust-lang/rust/blob/5d20ff4d2718c820632b38c1e49d4de648a9810b/src/libcore/internal_macros.rs | ||
1466 | parse_macro( | ||
1467 | r#" | ||
1468 | macro_rules! impl_fn_for_zst { | ||
1469 | { $( $( #[$attr: meta] )* | ||
1470 | struct $Name: ident impl$( <$( $lifetime : lifetime ),+> )? Fn = | ||
1471 | |$( $arg: ident: $ArgTy: ty ),*| -> $ReturnTy: ty | ||
1472 | $body: block; )+ | ||
1473 | } => { | ||
1474 | $( | ||
1475 | $( #[$attr] )* | ||
1476 | struct $Name; | ||
1477 | |||
1478 | impl $( <$( $lifetime ),+> )? Fn<($( $ArgTy, )*)> for $Name { | ||
1479 | #[inline] | ||
1480 | extern "rust-call" fn call(&self, ($( $arg, )*): ($( $ArgTy, )*)) -> $ReturnTy { | ||
1481 | $body | ||
1482 | } | ||
1483 | } | ||
1484 | |||
1485 | impl $( <$( $lifetime ),+> )? FnMut<($( $ArgTy, )*)> for $Name { | ||
1486 | #[inline] | ||
1487 | extern "rust-call" fn call_mut( | ||
1488 | &mut self, | ||
1489 | ($( $arg, )*): ($( $ArgTy, )*) | ||
1490 | ) -> $ReturnTy { | ||
1491 | Fn::call(&*self, ($( $arg, )*)) | ||
1492 | } | ||
1493 | } | ||
1494 | |||
1495 | impl $( <$( $lifetime ),+> )? FnOnce<($( $ArgTy, )*)> for $Name { | ||
1496 | type Output = $ReturnTy; | ||
1497 | |||
1498 | #[inline] | ||
1499 | extern "rust-call" fn call_once(self, ($( $arg, )*): ($( $ArgTy, )*)) -> $ReturnTy { | ||
1500 | Fn::call(&self, ($( $arg, )*)) | ||
1501 | } | ||
1502 | } | ||
1503 | )+ | ||
1504 | } | ||
1505 | } | ||
1506 | "#, | ||
1507 | ).assert_expand_items(r#" | ||
1508 | impl_fn_for_zst ! { | ||
1509 | # [ derive ( Clone ) ] | ||
1510 | struct CharEscapeDebugContinue impl Fn = | c : char | -> char :: EscapeDebug { | ||
1511 | c . escape_debug_ext ( false ) | ||
1512 | } ; | ||
1513 | |||
1514 | # [ derive ( Clone ) ] | ||
1515 | struct CharEscapeUnicode impl Fn = | c : char | -> char :: EscapeUnicode { | ||
1516 | c . escape_unicode ( ) | ||
1517 | } ; | ||
1518 | # [ derive ( Clone ) ] | ||
1519 | struct CharEscapeDefault impl Fn = | c : char | -> char :: EscapeDefault { | ||
1520 | c . escape_default ( ) | ||
1521 | } ; | ||
1522 | } | ||
1523 | "#, | ||
1524 | "# [derive (Clone)] struct CharEscapeDebugContinue ; impl Fn < (char ,) > for CharEscapeDebugContinue {# [inline] extern \"rust-call\" fn call (& self , (c ,) : (char ,)) -> char :: EscapeDebug {{c . escape_debug_ext (false)}}} impl FnMut < (char ,) > for CharEscapeDebugContinue {# [inline] extern \"rust-call\" fn call_mut (& mut self , (c ,) : (char ,)) -> char :: EscapeDebug {Fn :: call (&* self , (c ,))}} impl FnOnce < (char ,) > for CharEscapeDebugContinue {type Output = char :: EscapeDebug ; # [inline] extern \"rust-call\" fn call_once (self , (c ,) : (char ,)) -> char :: EscapeDebug {Fn :: call (& self , (c ,))}} # [derive (Clone)] struct CharEscapeUnicode ; impl Fn < (char ,) > for CharEscapeUnicode {# [inline] extern \"rust-call\" fn call (& self , (c ,) : (char ,)) -> char :: EscapeUnicode {{c . escape_unicode ()}}} impl FnMut < (char ,) > for CharEscapeUnicode {# [inline] extern \"rust-call\" fn call_mut (& mut self , (c ,) : (char ,)) -> char :: EscapeUnicode {Fn :: call (&* self , (c ,))}} impl FnOnce < (char ,) > for CharEscapeUnicode {type Output = char :: EscapeUnicode ; # [inline] extern \"rust-call\" fn call_once (self , (c ,) : (char ,)) -> char :: EscapeUnicode {Fn :: call (& self , (c ,))}} # [derive (Clone)] struct CharEscapeDefault ; impl Fn < (char ,) > for CharEscapeDefault {# [inline] extern \"rust-call\" fn call (& self , (c ,) : (char ,)) -> char :: EscapeDefault {{c . escape_default ()}}} impl FnMut < (char ,) > for CharEscapeDefault {# [inline] extern \"rust-call\" fn call_mut (& mut self , (c ,) : (char ,)) -> char :: EscapeDefault {Fn :: call (&* self , (c ,))}} impl FnOnce < (char ,) > for CharEscapeDefault {type Output = char :: EscapeDefault ; # [inline] extern \"rust-call\" fn call_once (self , (c ,) : (char ,)) -> char :: EscapeDefault {Fn :: call (& self , (c ,))}}" | ||
1525 | ); | ||
1526 | } | ||
1527 | |||
1528 | #[test] | ||
1529 | fn test_impl_nonzero_fmt() { | ||
1530 | // from https://github.com/rust-lang/rust/blob/316a391dcb7d66dc25f1f9a4ec9d368ef7615005/src/libcore/num/mod.rs#L12 | ||
1531 | parse_macro( | ||
1532 | r#" | ||
1533 | macro_rules! impl_nonzero_fmt { | ||
1534 | ( #[$stability: meta] ( $( $Trait: ident ),+ ) for $Ty: ident ) => { | ||
1535 | fn foo () {} | ||
1536 | } | ||
1537 | } | ||
1538 | "#, | ||
1539 | ).assert_expand_items( | ||
1540 | r#"impl_nonzero_fmt! { # [stable(feature= "nonzero",since="1.28.0")] (Debug,Display,Binary,Octal,LowerHex,UpperHex) for NonZeroU8}"#, | ||
1541 | "fn foo () {}", | ||
1542 | ); | ||
1543 | } | ||
1544 | |||
1545 | #[test] | ||
1546 | fn test_cfg_if_items() { | ||
1547 | // from https://github.com/rust-lang/rust/blob/33fe1131cadba69d317156847be9a402b89f11bb/src/libstd/macros.rs#L986 | ||
1548 | parse_macro( | ||
1549 | r#" | ||
1550 | macro_rules! __cfg_if_items { | ||
1551 | (($($not:meta,)*) ; ) => {}; | ||
1552 | (($($not:meta,)*) ; ( ($($m:meta),*) ($($it:item)*) ), $($rest:tt)*) => { | ||
1553 | __cfg_if_items! { ($($not,)* $($m,)*) ; $($rest)* } | ||
1554 | } | ||
1555 | } | ||
1556 | "#, | ||
1557 | ).assert_expand_items( | ||
1558 | r#"__cfg_if_items ! { ( rustdoc , ) ; ( ( ) ( # [ cfg ( any ( target_os = "redox" , unix ) ) ] # [ stable ( feature = "rust1" , since = "1.0.0" ) ] pub use sys :: ext as unix ; # [ cfg ( windows ) ] # [ stable ( feature = "rust1" , since = "1.0.0" ) ] pub use sys :: ext as windows ; # [ cfg ( any ( target_os = "linux" , target_os = "l4re" ) ) ] pub mod linux ; ) ) , }"#, | ||
1559 | "__cfg_if_items ! {(rustdoc ,) ;}", | ||
1560 | ); | ||
1561 | } | ||
1562 | |||
1563 | #[test] | ||
1564 | fn test_cfg_if_main() { | ||
1565 | // from https://github.com/rust-lang/rust/blob/3d211248393686e0f73851fc7548f6605220fbe1/src/libpanic_unwind/macros.rs#L9 | ||
1566 | parse_macro( | ||
1567 | r#" | ||
1568 | macro_rules! cfg_if { | ||
1569 | ($( | ||
1570 | if #[cfg($($meta:meta),*)] { $($it:item)* } | ||
1571 | ) else * else { | ||
1572 | $($it2:item)* | ||
1573 | }) => { | ||
1574 | __cfg_if_items! { | ||
1575 | () ; | ||
1576 | $( ( ($($meta),*) ($($it)*) ), )* | ||
1577 | ( () ($($it2)*) ), | ||
1578 | } | ||
1579 | }; | ||
1580 | |||
1581 | // Internal macro to Apply a cfg attribute to a list of items | ||
1582 | (@__apply $m:meta, $($it:item)*) => { | ||
1583 | $(#[$m] $it)* | ||
1584 | }; | ||
1585 | } | ||
1586 | "#, | ||
1587 | ).assert_expand_items(r#" | ||
1588 | cfg_if ! { | ||
1589 | if # [ cfg ( target_env = "msvc" ) ] { | ||
1590 | // no extra unwinder support needed | ||
1591 | } else if # [ cfg ( all ( target_arch = "wasm32" , not ( target_os = "emscripten" ) ) ) ] { | ||
1592 | // no unwinder on the system! | ||
1593 | } else { | ||
1594 | mod libunwind ; | ||
1595 | pub use libunwind :: * ; | ||
1596 | } | ||
1597 | } | ||
1598 | "#, | ||
1599 | "__cfg_if_items ! {() ; ((target_env = \"msvc\") ()) , ((all (target_arch = \"wasm32\" , not (target_os = \"emscripten\"))) ()) , (() (mod libunwind ; pub use libunwind :: * ;)) ,}" | ||
1600 | ).assert_expand_items( | ||
1601 | r#" | ||
1602 | cfg_if ! { @ __apply cfg ( all ( not ( any ( not ( any ( target_os = "solaris" , target_os = "illumos" ) ) ) ) ) ) , } | ||
1603 | "#, | ||
1604 | "", | ||
1605 | ); | ||
1606 | } | ||
1607 | |||
1608 | #[test] | ||
1609 | fn test_proptest_arbitrary() { | ||
1610 | // from https://github.com/AltSysrq/proptest/blob/d1c4b049337d2f75dd6f49a095115f7c532e5129/proptest/src/arbitrary/macros.rs#L16 | ||
1611 | parse_macro( | ||
1612 | r#" | ||
1613 | macro_rules! arbitrary { | ||
1614 | ([$($bounds : tt)*] $typ: ty, $strat: ty, $params: ty; | ||
1615 | $args: ident => $logic: expr) => { | ||
1616 | impl<$($bounds)*> $crate::arbitrary::Arbitrary for $typ { | ||
1617 | type Parameters = $params; | ||
1618 | type Strategy = $strat; | ||
1619 | fn arbitrary_with($args: Self::Parameters) -> Self::Strategy { | ||
1620 | $logic | ||
1621 | } | ||
1622 | } | ||
1623 | }; | ||
1624 | |||
1625 | }"#, | ||
1626 | ).assert_expand_items(r#"arbitrary ! ( [ A : Arbitrary ] | ||
1627 | Vec < A > , | ||
1628 | VecStrategy < A :: Strategy > , | ||
1629 | RangedParams1 < A :: Parameters > ; | ||
1630 | args => { let product_unpack ! [ range , a ] = args ; vec ( any_with :: < A > ( a ) , range ) } | ||
1631 | ) ;"#, | ||
1632 | "impl <A : Arbitrary > $crate :: arbitrary :: Arbitrary for Vec < A > {type Parameters = RangedParams1 < A :: Parameters > ; type Strategy = VecStrategy < A :: Strategy > ; fn arbitrary_with (args : Self :: Parameters) -> Self :: Strategy {{let product_unpack ! [range , a] = args ; vec (any_with :: < A > (a) , range)}}}" | ||
1633 | ); | ||
1634 | } | ||
1635 | |||
1636 | #[test] | ||
1637 | fn test_old_ridl() { | ||
1638 | // This is from winapi 2.8, which do not have a link from github | ||
1639 | // | ||
1640 | let expanded = parse_macro( | ||
1641 | r#" | ||
1642 | #[macro_export] | ||
1643 | macro_rules! RIDL { | ||
1644 | (interface $interface:ident ($vtbl:ident) : $pinterface:ident ($pvtbl:ident) | ||
1645 | {$( | ||
1646 | fn $method:ident(&mut self $(,$p:ident : $t:ty)*) -> $rtr:ty | ||
1647 | ),+} | ||
1648 | ) => { | ||
1649 | impl $interface { | ||
1650 | $(pub unsafe fn $method(&mut self) -> $rtr { | ||
1651 | ((*self.lpVtbl).$method)(self $(,$p)*) | ||
1652 | })+ | ||
1653 | } | ||
1654 | }; | ||
1655 | }"#, | ||
1656 | ).expand_tt(r#" | ||
1657 | RIDL!{interface ID3D11Asynchronous(ID3D11AsynchronousVtbl): ID3D11DeviceChild(ID3D11DeviceChildVtbl) { | ||
1658 | fn GetDataSize(&mut self) -> UINT | ||
1659 | }}"#); | ||
1660 | |||
1661 | assert_eq!(expanded.to_string(), "impl ID3D11Asynchronous {pub unsafe fn GetDataSize (& mut self) -> UINT {((* self . lpVtbl) .GetDataSize) (self)}}"); | ||
1662 | } | ||
1663 | |||
1664 | #[test] | ||
1665 | fn test_quick_error() { | ||
1666 | let expanded = parse_macro( | ||
1667 | r#" | ||
1668 | macro_rules! quick_error { | ||
1669 | |||
1670 | (SORT [enum $name:ident $( #[$meta:meta] )*] | ||
1671 | items [$($( #[$imeta:meta] )* | ||
1672 | => $iitem:ident: $imode:tt [$( $ivar:ident: $ityp:ty ),*] | ||
1673 | {$( $ifuncs:tt )*} )* ] | ||
1674 | buf [ ] | ||
1675 | queue [ ] | ||
1676 | ) => { | ||
1677 | quick_error!(ENUMINITION [enum $name $( #[$meta] )*] | ||
1678 | body [] | ||
1679 | queue [$( | ||
1680 | $( #[$imeta] )* | ||
1681 | => | ||
1682 | $iitem: $imode [$( $ivar: $ityp ),*] | ||
1683 | )*] | ||
1684 | ); | ||
1685 | }; | ||
1686 | |||
1687 | } | ||
1688 | "#, | ||
1689 | ) | ||
1690 | .expand_tt( | ||
1691 | r#" | ||
1692 | quick_error ! (SORT [enum Wrapped # [derive (Debug)]] items [ | ||
1693 | => One : UNIT [] {} | ||
1694 | => Two : TUPLE [s :String] {display ("two: {}" , s) from ()} | ||
1695 | ] buf [] queue []) ; | ||
1696 | "#, | ||
1697 | ); | ||
1698 | |||
1699 | assert_eq!(expanded.to_string(), "quick_error ! (ENUMINITION [enum Wrapped # [derive (Debug)]] body [] queue [=> One : UNIT [] => Two : TUPLE [s : String]]) ;"); | ||
1700 | } | ||
1701 | |||
1702 | #[test] | ||
1703 | fn test_empty_repeat_vars_in_empty_repeat_vars() { | ||
1704 | parse_macro( | ||
1705 | r#" | ||
1706 | macro_rules! delegate_impl { | ||
1707 | ([$self_type:ident, $self_wrap:ty, $self_map:ident] | ||
1708 | pub trait $name:ident $(: $sup:ident)* $(+ $more_sup:ident)* { | ||
1709 | |||
1710 | // "Escaped" associated types. Stripped before making the `trait` | ||
1711 | // itself, but forwarded when delegating impls. | ||
1712 | $( | ||
1713 | @escape [type $assoc_name_ext:ident] | ||
1714 | // Associated types. Forwarded. | ||
1715 | )* | ||
1716 | $( | ||
1717 | @section type | ||
1718 | $( | ||
1719 | $(#[$_assoc_attr:meta])* | ||
1720 | type $assoc_name:ident $(: $assoc_bound:ty)*; | ||
1721 | )+ | ||
1722 | )* | ||
1723 | // Methods. Forwarded. Using $self_map!(self) around the self argument. | ||
1724 | // Methods must use receiver `self` or explicit type like `self: &Self` | ||
1725 | // &self and &mut self are _not_ supported. | ||
1726 | $( | ||
1727 | @section self | ||
1728 | $( | ||
1729 | $(#[$_method_attr:meta])* | ||
1730 | fn $method_name:ident(self $(: $self_selftype:ty)* $(,$marg:ident : $marg_ty:ty)*) -> $mret:ty; | ||
1731 | )+ | ||
1732 | )* | ||
1733 | // Arbitrary tail that is ignored when forwarding. | ||
1734 | $( | ||
1735 | @section nodelegate | ||
1736 | $($tail:tt)* | ||
1737 | )* | ||
1738 | }) => { | ||
1739 | impl<> $name for $self_wrap where $self_type: $name { | ||
1740 | $( | ||
1741 | $( | ||
1742 | fn $method_name(self $(: $self_selftype)* $(,$marg: $marg_ty)*) -> $mret { | ||
1743 | $self_map!(self).$method_name($($marg),*) | ||
1744 | } | ||
1745 | )* | ||
1746 | )* | ||
1747 | } | ||
1748 | } | ||
1749 | } | ||
1750 | "#, | ||
1751 | ).assert_expand_items( | ||
1752 | r#"delegate_impl ! {[G , & 'a mut G , deref] pub trait Data : GraphBase {@ section type type NodeWeight ;}}"#, | ||
1753 | "impl <> Data for & \'a mut G where G : Data {}", | ||
1754 | ); | ||
1755 | } | ||
1756 | |||
1757 | #[test] | ||
1758 | fn expr_interpolation() { | ||
1759 | let expanded = parse_macro( | ||
1760 | r#" | ||
1761 | macro_rules! id { | ||
1762 | ($expr:expr) => { | ||
1763 | map($expr) | ||
1764 | } | ||
1765 | } | ||
1766 | "#, | ||
1767 | ) | ||
1768 | .expand_expr("id!(x + foo);"); | ||
1769 | |||
1770 | assert_eq!(expanded.to_string(), "map(x+foo)"); | ||
1771 | } | ||
1772 | |||
1773 | pub(crate) struct MacroFixture { | 12 | pub(crate) struct MacroFixture { |
1774 | rules: MacroRules, | 13 | rules: MacroRules, |
1775 | } | 14 | } |
@@ -1889,6 +128,37 @@ macro_rules! impl_fixture { | |||
1889 | impl_fixture!(MacroFixture); | 128 | impl_fixture!(MacroFixture); |
1890 | impl_fixture!(MacroFixture2); | 129 | impl_fixture!(MacroFixture2); |
1891 | 130 | ||
131 | pub(crate) fn parse_macro(ra_fixture: &str) -> MacroFixture { | ||
132 | let definition_tt = parse_macro_rules_to_tt(ra_fixture); | ||
133 | let rules = MacroRules::parse(&definition_tt).unwrap(); | ||
134 | MacroFixture { rules } | ||
135 | } | ||
136 | |||
137 | pub(crate) fn parse_macro2(ra_fixture: &str) -> MacroFixture2 { | ||
138 | let definition_tt = parse_macro_def_to_tt(ra_fixture); | ||
139 | let rules = MacroDef::parse(&definition_tt).unwrap(); | ||
140 | MacroFixture2 { rules } | ||
141 | } | ||
142 | |||
143 | pub(crate) fn parse_macro_error(ra_fixture: &str) -> ParseError { | ||
144 | let definition_tt = parse_macro_rules_to_tt(ra_fixture); | ||
145 | |||
146 | match MacroRules::parse(&definition_tt) { | ||
147 | Ok(_) => panic!("Expect error"), | ||
148 | Err(err) => err, | ||
149 | } | ||
150 | } | ||
151 | |||
152 | pub(crate) fn parse_to_token_tree_by_syntax(ra_fixture: &str) -> tt::Subtree { | ||
153 | let source_file = ast::SourceFile::parse(ra_fixture).ok().unwrap(); | ||
154 | let tt = syntax_node_to_token_tree(source_file.syntax()).unwrap().0; | ||
155 | |||
156 | let parsed = parse_to_token_tree(ra_fixture).unwrap().0; | ||
157 | assert_eq!(tt, parsed); | ||
158 | |||
159 | parsed | ||
160 | } | ||
161 | |||
1892 | fn parse_macro_rules_to_tt(ra_fixture: &str) -> tt::Subtree { | 162 | fn parse_macro_rules_to_tt(ra_fixture: &str) -> tt::Subtree { |
1893 | let source_file = ast::SourceFile::parse(ra_fixture).ok().unwrap(); | 163 | let source_file = ast::SourceFile::parse(ra_fixture).ok().unwrap(); |
1894 | let macro_definition = | 164 | let macro_definition = |
@@ -1922,37 +192,6 @@ fn parse_macro_def_to_tt(ra_fixture: &str) -> tt::Subtree { | |||
1922 | definition_tt | 192 | definition_tt |
1923 | } | 193 | } |
1924 | 194 | ||
1925 | pub(crate) fn parse_macro(ra_fixture: &str) -> MacroFixture { | ||
1926 | let definition_tt = parse_macro_rules_to_tt(ra_fixture); | ||
1927 | let rules = MacroRules::parse(&definition_tt).unwrap(); | ||
1928 | MacroFixture { rules } | ||
1929 | } | ||
1930 | |||
1931 | pub(crate) fn parse_macro2(ra_fixture: &str) -> MacroFixture2 { | ||
1932 | let definition_tt = parse_macro_def_to_tt(ra_fixture); | ||
1933 | let rules = MacroDef::parse(&definition_tt).unwrap(); | ||
1934 | MacroFixture2 { rules } | ||
1935 | } | ||
1936 | |||
1937 | pub(crate) fn parse_macro_error(ra_fixture: &str) -> ParseError { | ||
1938 | let definition_tt = parse_macro_rules_to_tt(ra_fixture); | ||
1939 | |||
1940 | match MacroRules::parse(&definition_tt) { | ||
1941 | Ok(_) => panic!("Expect error"), | ||
1942 | Err(err) => err, | ||
1943 | } | ||
1944 | } | ||
1945 | |||
1946 | pub(crate) fn parse_to_token_tree_by_syntax(ra_fixture: &str) -> tt::Subtree { | ||
1947 | let source_file = ast::SourceFile::parse(ra_fixture).ok().unwrap(); | ||
1948 | let tt = syntax_node_to_token_tree(source_file.syntax()).unwrap().0; | ||
1949 | |||
1950 | let parsed = parse_to_token_tree(ra_fixture).unwrap().0; | ||
1951 | assert_eq!(tt, parsed); | ||
1952 | |||
1953 | parsed | ||
1954 | } | ||
1955 | |||
1956 | fn debug_dump_ignore_spaces(node: &syntax::SyntaxNode) -> String { | 195 | fn debug_dump_ignore_spaces(node: &syntax::SyntaxNode) -> String { |
1957 | let mut level = 0; | 196 | let mut level = 0; |
1958 | let mut buf = String::new(); | 197 | let mut buf = String::new(); |
@@ -1988,166 +227,3 @@ fn debug_dump_ignore_spaces(node: &syntax::SyntaxNode) -> String { | |||
1988 | 227 | ||
1989 | buf | 228 | buf |
1990 | } | 229 | } |
1991 | |||
1992 | #[test] | ||
1993 | fn test_issue_2520() { | ||
1994 | let macro_fixture = parse_macro( | ||
1995 | r#" | ||
1996 | macro_rules! my_macro { | ||
1997 | { | ||
1998 | ( $( | ||
1999 | $( [] $sname:ident : $stype:ty )? | ||
2000 | $( [$expr:expr] $nname:ident : $ntype:ty )? | ||
2001 | ),* ) | ||
2002 | } => { | ||
2003 | Test { | ||
2004 | $( | ||
2005 | $( $sname, )? | ||
2006 | )* | ||
2007 | } | ||
2008 | }; | ||
2009 | } | ||
2010 | "#, | ||
2011 | ); | ||
2012 | |||
2013 | macro_fixture.assert_expand_items( | ||
2014 | r#"my_macro ! { | ||
2015 | ([] p1 : u32 , [|_| S0K0] s : S0K0 , [] k0 : i32) | ||
2016 | }"#, | ||
2017 | "Test {p1 , k0 ,}", | ||
2018 | ); | ||
2019 | } | ||
2020 | |||
2021 | #[test] | ||
2022 | fn test_issue_3861() { | ||
2023 | let macro_fixture = parse_macro( | ||
2024 | r#" | ||
2025 | macro_rules! rgb_color { | ||
2026 | ($p:expr, $t: ty) => { | ||
2027 | pub fn new() { | ||
2028 | let _ = 0 as $t << $p; | ||
2029 | } | ||
2030 | }; | ||
2031 | } | ||
2032 | "#, | ||
2033 | ); | ||
2034 | |||
2035 | macro_fixture.expand_items(r#"rgb_color!(8 + 8, u32);"#); | ||
2036 | } | ||
2037 | |||
2038 | #[test] | ||
2039 | fn test_repeat_bad_var() { | ||
2040 | // FIXME: the second rule of the macro should be removed and an error about | ||
2041 | // `$( $c )+` raised | ||
2042 | parse_macro( | ||
2043 | r#" | ||
2044 | macro_rules! foo { | ||
2045 | ($( $b:ident )+) => { | ||
2046 | $( $c )+ | ||
2047 | }; | ||
2048 | ($( $b:ident )+) => { | ||
2049 | $( $b )+ | ||
2050 | } | ||
2051 | } | ||
2052 | "#, | ||
2053 | ) | ||
2054 | .assert_expand_items("foo!(b0 b1);", "b0 b1"); | ||
2055 | } | ||
2056 | |||
2057 | #[test] | ||
2058 | fn test_no_space_after_semi_colon() { | ||
2059 | let expanded = parse_macro( | ||
2060 | r#" | ||
2061 | macro_rules! with_std { ($($i:item)*) => ($(#[cfg(feature = "std")]$i)*) } | ||
2062 | "#, | ||
2063 | ) | ||
2064 | .expand_items(r#"with_std! {mod m;mod f;}"#); | ||
2065 | |||
2066 | let dump = format!("{:#?}", expanded); | ||
2067 | assert_eq_text!( | ||
2068 | r###"[email protected] | ||
2069 | [email protected] | ||
2070 | [email protected] | ||
2071 | [email protected] "#" | ||
2072 | [email protected] "[" | ||
2073 | [email protected] | ||
2074 | [email protected] | ||
2075 | [email protected] | ||
2076 | [email protected] "cfg" | ||
2077 | [email protected] | ||
2078 | [email protected] "(" | ||
2079 | [email protected] "feature" | ||
2080 | [email protected] "=" | ||
2081 | [email protected] "\"std\"" | ||
2082 | [email protected] ")" | ||
2083 | [email protected] "]" | ||
2084 | [email protected] "mod" | ||
2085 | [email protected] | ||
2086 | [email protected] "m" | ||
2087 | [email protected] ";" | ||
2088 | [email protected] | ||
2089 | [email protected] | ||
2090 | [email protected] "#" | ||
2091 | [email protected] "[" | ||
2092 | [email protected] | ||
2093 | [email protected] | ||
2094 | [email protected] | ||
2095 | [email protected] "cfg" | ||
2096 | [email protected] | ||
2097 | [email protected] "(" | ||
2098 | [email protected] "feature" | ||
2099 | [email protected] "=" | ||
2100 | [email protected] "\"std\"" | ||
2101 | [email protected] ")" | ||
2102 | [email protected] "]" | ||
2103 | [email protected] "mod" | ||
2104 | [email protected] | ||
2105 | [email protected] "f" | ||
2106 | [email protected] ";""###, | ||
2107 | dump.trim() | ||
2108 | ); | ||
2109 | } | ||
2110 | |||
2111 | // https://github.com/rust-lang/rust/blob/master/src/test/ui/issues/issue-57597.rs | ||
2112 | #[test] | ||
2113 | fn test_rustc_issue_57597() { | ||
2114 | fn test_error(fixture: &str) { | ||
2115 | assert_eq!(parse_macro_error(fixture), ParseError::RepetitionEmptyTokenTree); | ||
2116 | } | ||
2117 | |||
2118 | test_error("macro_rules! foo { ($($($i:ident)?)+) => {}; }"); | ||
2119 | test_error("macro_rules! foo { ($($($i:ident)?)*) => {}; }"); | ||
2120 | test_error("macro_rules! foo { ($($($i:ident)?)?) => {}; }"); | ||
2121 | test_error("macro_rules! foo { ($($($($i:ident)?)?)?) => {}; }"); | ||
2122 | test_error("macro_rules! foo { ($($($($i:ident)*)?)?) => {}; }"); | ||
2123 | test_error("macro_rules! foo { ($($($($i:ident)?)*)?) => {}; }"); | ||
2124 | test_error("macro_rules! foo { ($($($($i:ident)?)?)*) => {}; }"); | ||
2125 | test_error("macro_rules! foo { ($($($($i:ident)*)*)?) => {}; }"); | ||
2126 | test_error("macro_rules! foo { ($($($($i:ident)?)*)*) => {}; }"); | ||
2127 | test_error("macro_rules! foo { ($($($($i:ident)?)*)+) => {}; }"); | ||
2128 | test_error("macro_rules! foo { ($($($($i:ident)+)?)*) => {}; }"); | ||
2129 | test_error("macro_rules! foo { ($($($($i:ident)+)*)?) => {}; }"); | ||
2130 | } | ||
2131 | |||
2132 | #[test] | ||
2133 | fn test_expand_bad_literal() { | ||
2134 | parse_macro( | ||
2135 | r#" | ||
2136 | macro_rules! foo { ($i:literal) => {}; } | ||
2137 | "#, | ||
2138 | ) | ||
2139 | .assert_expand_err(r#"foo!(&k");"#, &ExpandError::BindingError("".into())); | ||
2140 | } | ||
2141 | |||
2142 | #[test] | ||
2143 | fn test_empty_comments() { | ||
2144 | parse_macro( | ||
2145 | r#" | ||
2146 | macro_rules! one_arg_macro { ($fmt:expr) => (); } | ||
2147 | "#, | ||
2148 | ) | ||
2149 | .assert_expand_err( | ||
2150 | r#"one_arg_macro!(/**/)"#, | ||
2151 | &ExpandError::BindingError("expected Expr".into()), | ||
2152 | ); | ||
2153 | } | ||
diff --git a/crates/mbe/src/tests/expand.rs b/crates/mbe/src/tests/expand.rs new file mode 100644 index 000000000..9dd8ff75b --- /dev/null +++ b/crates/mbe/src/tests/expand.rs | |||
@@ -0,0 +1,1872 @@ | |||
1 | use ::parser::FragmentKind; | ||
2 | use syntax::{ | ||
3 | SyntaxKind::{ERROR, IDENT}, | ||
4 | T, | ||
5 | }; | ||
6 | use test_utils::assert_eq_text; | ||
7 | |||
8 | use super::*; | ||
9 | |||
10 | // Good first issue (although a slightly challenging one): | ||
11 | // | ||
12 | // * Pick a random test from here | ||
13 | // https://github.com/intellij-rust/intellij-rust/blob/c4e9feee4ad46e7953b1948c112533360b6087bb/src/test/kotlin/org/rust/lang/core/macros/RsMacroExpansionTest.kt | ||
14 | // * Port the test to rust and add it to this module | ||
15 | // * Make it pass :-) | ||
16 | |||
17 | #[test] | ||
18 | fn test_token_id_shift() { | ||
19 | let expansion = parse_macro( | ||
20 | r#" | ||
21 | macro_rules! foobar { | ||
22 | ($e:ident) => { foo bar $e } | ||
23 | } | ||
24 | "#, | ||
25 | ) | ||
26 | .expand_tt("foobar!(baz);"); | ||
27 | |||
28 | fn get_id(t: &tt::TokenTree) -> Option<u32> { | ||
29 | if let tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) = t { | ||
30 | return Some(ident.id.0); | ||
31 | } | ||
32 | None | ||
33 | } | ||
34 | |||
35 | assert_eq!(expansion.token_trees.len(), 3); | ||
36 | // {($e:ident) => { foo bar $e }} | ||
37 | // 012345 67 8 9 T 12 | ||
38 | assert_eq!(get_id(&expansion.token_trees[0]), Some(9)); | ||
39 | assert_eq!(get_id(&expansion.token_trees[1]), Some(10)); | ||
40 | |||
41 | // The input args of macro call include parentheses: | ||
42 | // (baz) | ||
43 | // So baz should be 12+1+1 | ||
44 | assert_eq!(get_id(&expansion.token_trees[2]), Some(14)); | ||
45 | } | ||
46 | |||
47 | #[test] | ||
48 | fn test_token_map() { | ||
49 | let expanded = parse_macro( | ||
50 | r#" | ||
51 | macro_rules! foobar { | ||
52 | ($e:ident) => { fn $e() {} } | ||
53 | } | ||
54 | "#, | ||
55 | ) | ||
56 | .expand_tt("foobar!(baz);"); | ||
57 | |||
58 | let (node, token_map) = token_tree_to_syntax_node(&expanded, FragmentKind::Items).unwrap(); | ||
59 | let content = node.syntax_node().to_string(); | ||
60 | |||
61 | let get_text = |id, kind| -> String { | ||
62 | content[token_map.range_by_token(id).unwrap().by_kind(kind).unwrap()].to_string() | ||
63 | }; | ||
64 | |||
65 | assert_eq!(expanded.token_trees.len(), 4); | ||
66 | // {($e:ident) => { fn $e() {} }} | ||
67 | // 012345 67 8 9 T12 3 | ||
68 | |||
69 | assert_eq!(get_text(tt::TokenId(9), IDENT), "fn"); | ||
70 | assert_eq!(get_text(tt::TokenId(12), T!['(']), "("); | ||
71 | assert_eq!(get_text(tt::TokenId(13), T!['{']), "{"); | ||
72 | } | ||
73 | |||
74 | #[test] | ||
75 | fn test_convert_tt() { | ||
76 | parse_macro(r#" | ||
77 | macro_rules! impl_froms { | ||
78 | ($e:ident: $($v:ident),*) => { | ||
79 | $( | ||
80 | impl From<$v> for $e { | ||
81 | fn from(it: $v) -> $e { | ||
82 | $e::$v(it) | ||
83 | } | ||
84 | } | ||
85 | )* | ||
86 | } | ||
87 | } | ||
88 | "#) | ||
89 | .assert_expand_tt( | ||
90 | "impl_froms!(TokenTree: Leaf, Subtree);", | ||
91 | "impl From <Leaf > for TokenTree {fn from (it : Leaf) -> TokenTree {TokenTree ::Leaf (it)}} \ | ||
92 | impl From <Subtree > for TokenTree {fn from (it : Subtree) -> TokenTree {TokenTree ::Subtree (it)}}" | ||
93 | ); | ||
94 | } | ||
95 | |||
96 | #[test] | ||
97 | fn test_convert_tt2() { | ||
98 | parse_macro( | ||
99 | r#" | ||
100 | macro_rules! impl_froms { | ||
101 | ($e:ident: $($v:ident),*) => { | ||
102 | $( | ||
103 | impl From<$v> for $e { | ||
104 | fn from(it: $v) -> $e { | ||
105 | $e::$v(it) | ||
106 | } | ||
107 | } | ||
108 | )* | ||
109 | } | ||
110 | } | ||
111 | "#, | ||
112 | ) | ||
113 | .assert_expand( | ||
114 | "impl_froms!(TokenTree: Leaf, Subtree);", | ||
115 | r#" | ||
116 | SUBTREE $ | ||
117 | IDENT impl 20 | ||
118 | IDENT From 21 | ||
119 | PUNCH < [joint] 22 | ||
120 | IDENT Leaf 53 | ||
121 | PUNCH > [alone] 25 | ||
122 | IDENT for 26 | ||
123 | IDENT TokenTree 51 | ||
124 | SUBTREE {} 29 | ||
125 | IDENT fn 30 | ||
126 | IDENT from 31 | ||
127 | SUBTREE () 32 | ||
128 | IDENT it 33 | ||
129 | PUNCH : [alone] 34 | ||
130 | IDENT Leaf 53 | ||
131 | PUNCH - [joint] 37 | ||
132 | PUNCH > [alone] 38 | ||
133 | IDENT TokenTree 51 | ||
134 | SUBTREE {} 41 | ||
135 | IDENT TokenTree 51 | ||
136 | PUNCH : [joint] 44 | ||
137 | PUNCH : [joint] 45 | ||
138 | IDENT Leaf 53 | ||
139 | SUBTREE () 48 | ||
140 | IDENT it 49 | ||
141 | IDENT impl 20 | ||
142 | IDENT From 21 | ||
143 | PUNCH < [joint] 22 | ||
144 | IDENT Subtree 55 | ||
145 | PUNCH > [alone] 25 | ||
146 | IDENT for 26 | ||
147 | IDENT TokenTree 51 | ||
148 | SUBTREE {} 29 | ||
149 | IDENT fn 30 | ||
150 | IDENT from 31 | ||
151 | SUBTREE () 32 | ||
152 | IDENT it 33 | ||
153 | PUNCH : [alone] 34 | ||
154 | IDENT Subtree 55 | ||
155 | PUNCH - [joint] 37 | ||
156 | PUNCH > [alone] 38 | ||
157 | IDENT TokenTree 51 | ||
158 | SUBTREE {} 41 | ||
159 | IDENT TokenTree 51 | ||
160 | PUNCH : [joint] 44 | ||
161 | PUNCH : [joint] 45 | ||
162 | IDENT Subtree 55 | ||
163 | SUBTREE () 48 | ||
164 | IDENT it 49 | ||
165 | "#, | ||
166 | ); | ||
167 | } | ||
168 | |||
169 | #[test] | ||
170 | fn test_lifetime_split() { | ||
171 | parse_macro( | ||
172 | r#" | ||
173 | macro_rules! foo { | ||
174 | ($($t:tt)*) => { $($t)*} | ||
175 | } | ||
176 | "#, | ||
177 | ) | ||
178 | .assert_expand( | ||
179 | r#"foo!(static bar: &'static str = "hello";);"#, | ||
180 | r#" | ||
181 | SUBTREE $ | ||
182 | IDENT static 17 | ||
183 | IDENT bar 18 | ||
184 | PUNCH : [alone] 19 | ||
185 | PUNCH & [alone] 20 | ||
186 | PUNCH ' [joint] 21 | ||
187 | IDENT static 22 | ||
188 | IDENT str 23 | ||
189 | PUNCH = [alone] 24 | ||
190 | LITERAL "hello" 25 | ||
191 | PUNCH ; [joint] 26 | ||
192 | "#, | ||
193 | ); | ||
194 | } | ||
195 | |||
196 | #[test] | ||
197 | fn test_expr_order() { | ||
198 | let expanded = parse_macro( | ||
199 | r#" | ||
200 | macro_rules! foo { | ||
201 | ($ i:expr) => { | ||
202 | fn bar() { $ i * 2; } | ||
203 | } | ||
204 | } | ||
205 | "#, | ||
206 | ) | ||
207 | .expand_items("foo! { 1 + 1}"); | ||
208 | |||
209 | let dump = format!("{:#?}", expanded); | ||
210 | assert_eq_text!( | ||
211 | r#"[email protected] | ||
212 | [email protected] | ||
213 | [email protected] "fn" | ||
214 | [email protected] | ||
215 | [email protected] "bar" | ||
216 | [email protected] | ||
217 | [email protected] "(" | ||
218 | [email protected] ")" | ||
219 | [email protected] | ||
220 | [email protected] "{" | ||
221 | [email protected] | ||
222 | [email protected] | ||
223 | [email protected] | ||
224 | [email protected] | ||
225 | [email protected] "1" | ||
226 | [email protected] "+" | ||
227 | [email protected] | ||
228 | [email protected] "1" | ||
229 | [email protected] "*" | ||
230 | [email protected] | ||
231 | [email protected] "2" | ||
232 | [email protected] ";" | ||
233 | [email protected] "}""#, | ||
234 | dump.trim() | ||
235 | ); | ||
236 | } | ||
237 | |||
238 | #[test] | ||
239 | fn test_fail_match_pattern_by_first_token() { | ||
240 | parse_macro( | ||
241 | r#" | ||
242 | macro_rules! foo { | ||
243 | ($ i:ident) => ( | ||
244 | mod $ i {} | ||
245 | ); | ||
246 | (= $ i:ident) => ( | ||
247 | fn $ i() {} | ||
248 | ); | ||
249 | (+ $ i:ident) => ( | ||
250 | struct $ i; | ||
251 | ) | ||
252 | } | ||
253 | "#, | ||
254 | ) | ||
255 | .assert_expand_items("foo! { foo }", "mod foo {}") | ||
256 | .assert_expand_items("foo! { = bar }", "fn bar () {}") | ||
257 | .assert_expand_items("foo! { + Baz }", "struct Baz ;"); | ||
258 | } | ||
259 | |||
260 | #[test] | ||
261 | fn test_fail_match_pattern_by_last_token() { | ||
262 | parse_macro( | ||
263 | r#" | ||
264 | macro_rules! foo { | ||
265 | ($ i:ident) => ( | ||
266 | mod $ i {} | ||
267 | ); | ||
268 | ($ i:ident =) => ( | ||
269 | fn $ i() {} | ||
270 | ); | ||
271 | ($ i:ident +) => ( | ||
272 | struct $ i; | ||
273 | ) | ||
274 | } | ||
275 | "#, | ||
276 | ) | ||
277 | .assert_expand_items("foo! { foo }", "mod foo {}") | ||
278 | .assert_expand_items("foo! { bar = }", "fn bar () {}") | ||
279 | .assert_expand_items("foo! { Baz + }", "struct Baz ;"); | ||
280 | } | ||
281 | |||
282 | #[test] | ||
283 | fn test_fail_match_pattern_by_word_token() { | ||
284 | parse_macro( | ||
285 | r#" | ||
286 | macro_rules! foo { | ||
287 | ($ i:ident) => ( | ||
288 | mod $ i {} | ||
289 | ); | ||
290 | (spam $ i:ident) => ( | ||
291 | fn $ i() {} | ||
292 | ); | ||
293 | (eggs $ i:ident) => ( | ||
294 | struct $ i; | ||
295 | ) | ||
296 | } | ||
297 | "#, | ||
298 | ) | ||
299 | .assert_expand_items("foo! { foo }", "mod foo {}") | ||
300 | .assert_expand_items("foo! { spam bar }", "fn bar () {}") | ||
301 | .assert_expand_items("foo! { eggs Baz }", "struct Baz ;"); | ||
302 | } | ||
303 | |||
304 | #[test] | ||
305 | fn test_match_group_pattern_by_separator_token() { | ||
306 | parse_macro( | ||
307 | r#" | ||
308 | macro_rules! foo { | ||
309 | ($ ($ i:ident),*) => ($ ( | ||
310 | mod $ i {} | ||
311 | )*); | ||
312 | ($ ($ i:ident)#*) => ($ ( | ||
313 | fn $ i() {} | ||
314 | )*); | ||
315 | ($ i:ident ,# $ j:ident) => ( | ||
316 | struct $ i; | ||
317 | struct $ j; | ||
318 | ) | ||
319 | } | ||
320 | "#, | ||
321 | ) | ||
322 | .assert_expand_items("foo! { foo, bar }", "mod foo {} mod bar {}") | ||
323 | .assert_expand_items("foo! { foo# bar }", "fn foo () {} fn bar () {}") | ||
324 | .assert_expand_items("foo! { Foo,# Bar }", "struct Foo ; struct Bar ;"); | ||
325 | } | ||
326 | |||
327 | #[test] | ||
328 | fn test_match_group_pattern_with_multiple_defs() { | ||
329 | parse_macro( | ||
330 | r#" | ||
331 | macro_rules! foo { | ||
332 | ($ ($ i:ident),*) => ( struct Bar { $ ( | ||
333 | fn $ i {} | ||
334 | )*} ); | ||
335 | } | ||
336 | "#, | ||
337 | ) | ||
338 | .assert_expand_items("foo! { foo, bar }", "struct Bar {fn foo {} fn bar {}}"); | ||
339 | } | ||
340 | |||
341 | #[test] | ||
342 | fn test_match_group_pattern_with_multiple_statement() { | ||
343 | parse_macro( | ||
344 | r#" | ||
345 | macro_rules! foo { | ||
346 | ($ ($ i:ident),*) => ( fn baz { $ ( | ||
347 | $ i (); | ||
348 | )*} ); | ||
349 | } | ||
350 | "#, | ||
351 | ) | ||
352 | .assert_expand_items("foo! { foo, bar }", "fn baz {foo () ; bar () ;}"); | ||
353 | } | ||
354 | |||
355 | #[test] | ||
356 | fn test_match_group_pattern_with_multiple_statement_without_semi() { | ||
357 | parse_macro( | ||
358 | r#" | ||
359 | macro_rules! foo { | ||
360 | ($ ($ i:ident),*) => ( fn baz { $ ( | ||
361 | $i() | ||
362 | );*} ); | ||
363 | } | ||
364 | "#, | ||
365 | ) | ||
366 | .assert_expand_items("foo! { foo, bar }", "fn baz {foo () ;bar ()}"); | ||
367 | } | ||
368 | |||
369 | #[test] | ||
370 | fn test_match_group_empty_fixed_token() { | ||
371 | parse_macro( | ||
372 | r#" | ||
373 | macro_rules! foo { | ||
374 | ($ ($ i:ident)* #abc) => ( fn baz { $ ( | ||
375 | $ i (); | ||
376 | )*} ); | ||
377 | } | ||
378 | "#, | ||
379 | ) | ||
380 | .assert_expand_items("foo! {#abc}", "fn baz {}"); | ||
381 | } | ||
382 | |||
383 | #[test] | ||
384 | fn test_match_group_in_subtree() { | ||
385 | parse_macro( | ||
386 | r#" | ||
387 | macro_rules! foo { | ||
388 | (fn $name:ident {$($i:ident)*} ) => ( fn $name() { $ ( | ||
389 | $ i (); | ||
390 | )*} ); | ||
391 | }"#, | ||
392 | ) | ||
393 | .assert_expand_items("foo! {fn baz {a b} }", "fn baz () {a () ; b () ;}"); | ||
394 | } | ||
395 | |||
396 | #[test] | ||
397 | fn test_match_group_with_multichar_sep() { | ||
398 | parse_macro( | ||
399 | r#" | ||
400 | macro_rules! foo { | ||
401 | (fn $name:ident {$($i:literal)*} ) => ( fn $name() -> bool { $($i)&&*} ); | ||
402 | }"#, | ||
403 | ) | ||
404 | .assert_expand_items("foo! (fn baz {true true} );", "fn baz () -> bool {true &&true}"); | ||
405 | } | ||
406 | |||
407 | #[test] | ||
408 | fn test_match_group_with_multichar_sep2() { | ||
409 | parse_macro( | ||
410 | r#" | ||
411 | macro_rules! foo { | ||
412 | (fn $name:ident {$($i:literal)&&*} ) => ( fn $name() -> bool { $($i)&&*} ); | ||
413 | }"#, | ||
414 | ) | ||
415 | .assert_expand_items("foo! (fn baz {true && true} );", "fn baz () -> bool {true &&true}"); | ||
416 | } | ||
417 | |||
418 | #[test] | ||
419 | fn test_match_group_zero_match() { | ||
420 | parse_macro( | ||
421 | r#" | ||
422 | macro_rules! foo { | ||
423 | ( $($i:ident)* ) => (); | ||
424 | }"#, | ||
425 | ) | ||
426 | .assert_expand_items("foo! ();", ""); | ||
427 | } | ||
428 | |||
429 | #[test] | ||
430 | fn test_match_group_in_group() { | ||
431 | parse_macro( | ||
432 | r#" | ||
433 | macro_rules! foo { | ||
434 | { $( ( $($i:ident)* ) )* } => ( $( ( $($i)* ) )* ); | ||
435 | }"#, | ||
436 | ) | ||
437 | .assert_expand_items("foo! ( (a b) );", "(a b)"); | ||
438 | } | ||
439 | |||
440 | #[test] | ||
441 | fn test_expand_to_item_list() { | ||
442 | let tree = parse_macro( | ||
443 | " | ||
444 | macro_rules! structs { | ||
445 | ($($i:ident),*) => { | ||
446 | $(struct $i { field: u32 } )* | ||
447 | } | ||
448 | } | ||
449 | ", | ||
450 | ) | ||
451 | .expand_items("structs!(Foo, Bar);"); | ||
452 | assert_eq!( | ||
453 | format!("{:#?}", tree).trim(), | ||
454 | r#" | ||
455 | [email protected] | ||
456 | [email protected] | ||
457 | [email protected] "struct" | ||
458 | [email protected] | ||
459 | [email protected] "Foo" | ||
460 | [email protected] | ||
461 | [email protected] "{" | ||
462 | [email protected] | ||
463 | [email protected] | ||
464 | [email protected] "field" | ||
465 | [email protected] ":" | ||
466 | [email protected] | ||
467 | [email protected] | ||
468 | [email protected] | ||
469 | [email protected] | ||
470 | [email protected] "u32" | ||
471 | [email protected] "}" | ||
472 | [email protected] | ||
473 | [email protected] "struct" | ||
474 | [email protected] | ||
475 | [email protected] "Bar" | ||
476 | [email protected] | ||
477 | [email protected] "{" | ||
478 | [email protected] | ||
479 | [email protected] | ||
480 | [email protected] "field" | ||
481 | [email protected] ":" | ||
482 | [email protected] | ||
483 | [email protected] | ||
484 | [email protected] | ||
485 | [email protected] | ||
486 | [email protected] "u32" | ||
487 | [email protected] "}""# | ||
488 | .trim() | ||
489 | ); | ||
490 | } | ||
491 | |||
492 | fn to_subtree(tt: &tt::TokenTree) -> &tt::Subtree { | ||
493 | if let tt::TokenTree::Subtree(subtree) = tt { | ||
494 | return &subtree; | ||
495 | } | ||
496 | unreachable!("It is not a subtree"); | ||
497 | } | ||
498 | fn to_literal(tt: &tt::TokenTree) -> &tt::Literal { | ||
499 | if let tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) = tt { | ||
500 | return lit; | ||
501 | } | ||
502 | unreachable!("It is not a literal"); | ||
503 | } | ||
504 | |||
505 | fn to_punct(tt: &tt::TokenTree) -> &tt::Punct { | ||
506 | if let tt::TokenTree::Leaf(tt::Leaf::Punct(lit)) = tt { | ||
507 | return lit; | ||
508 | } | ||
509 | unreachable!("It is not a Punct"); | ||
510 | } | ||
511 | |||
512 | #[test] | ||
513 | fn test_expand_literals_to_token_tree() { | ||
514 | let expansion = parse_macro( | ||
515 | r#" | ||
516 | macro_rules! literals { | ||
517 | ($i:ident) => { | ||
518 | { | ||
519 | let a = 'c'; | ||
520 | let c = 1000; | ||
521 | let f = 12E+99_f64; | ||
522 | let s = "rust1"; | ||
523 | } | ||
524 | } | ||
525 | } | ||
526 | "#, | ||
527 | ) | ||
528 | .expand_tt("literals!(foo);"); | ||
529 | let stm_tokens = &to_subtree(&expansion.token_trees[0]).token_trees; | ||
530 | |||
531 | // [let] [a] [=] ['c'] [;] | ||
532 | assert_eq!(to_literal(&stm_tokens[3]).text, "'c'"); | ||
533 | // [let] [c] [=] [1000] [;] | ||
534 | assert_eq!(to_literal(&stm_tokens[5 + 3]).text, "1000"); | ||
535 | // [let] [f] [=] [12E+99_f64] [;] | ||
536 | assert_eq!(to_literal(&stm_tokens[10 + 3]).text, "12E+99_f64"); | ||
537 | // [let] [s] [=] ["rust1"] [;] | ||
538 | assert_eq!(to_literal(&stm_tokens[15 + 3]).text, "\"rust1\""); | ||
539 | } | ||
540 | |||
541 | #[test] | ||
542 | fn test_attr_to_token_tree() { | ||
543 | let expansion = parse_to_token_tree_by_syntax( | ||
544 | r#" | ||
545 | #[derive(Copy)] | ||
546 | struct Foo; | ||
547 | "#, | ||
548 | ); | ||
549 | |||
550 | assert_eq!(to_punct(&expansion.token_trees[0]).char, '#'); | ||
551 | assert_eq!( | ||
552 | to_subtree(&expansion.token_trees[1]).delimiter_kind(), | ||
553 | Some(tt::DelimiterKind::Bracket) | ||
554 | ); | ||
555 | } | ||
556 | |||
557 | #[test] | ||
558 | fn test_two_idents() { | ||
559 | parse_macro( | ||
560 | r#" | ||
561 | macro_rules! foo { | ||
562 | ($ i:ident, $ j:ident) => { | ||
563 | fn foo() { let a = $ i; let b = $j; } | ||
564 | } | ||
565 | } | ||
566 | "#, | ||
567 | ) | ||
568 | .assert_expand_items("foo! { foo, bar }", "fn foo () {let a = foo ; let b = bar ;}"); | ||
569 | } | ||
570 | |||
571 | #[test] | ||
572 | fn test_tt_to_stmts() { | ||
573 | let stmts = parse_macro( | ||
574 | r#" | ||
575 | macro_rules! foo { | ||
576 | () => { | ||
577 | let a = 0; | ||
578 | a = 10 + 1; | ||
579 | a | ||
580 | } | ||
581 | } | ||
582 | "#, | ||
583 | ) | ||
584 | .expand_statements("foo!{}"); | ||
585 | |||
586 | assert_eq!( | ||
587 | format!("{:#?}", stmts).trim(), | ||
588 | r#"[email protected] | ||
589 | [email protected] | ||
590 | [email protected] "let" | ||
591 | [email protected] | ||
592 | [email protected] | ||
593 | [email protected] "a" | ||
594 | [email protected] "=" | ||
595 | [email protected] | ||
596 | [email protected] "0" | ||
597 | [email protected] ";" | ||
598 | [email protected] | ||
599 | [email protected] | ||
600 | [email protected] | ||
601 | [email protected] | ||
602 | [email protected] | ||
603 | [email protected] | ||
604 | [email protected] "a" | ||
605 | [email protected] "=" | ||
606 | [email protected] | ||
607 | [email protected] | ||
608 | [email protected] "10" | ||
609 | [email protected] "+" | ||
610 | [email protected] | ||
611 | [email protected] "1" | ||
612 | [email protected] ";" | ||
613 | [email protected] | ||
614 | [email protected] | ||
615 | [email protected] | ||
616 | [email protected] | ||
617 | [email protected] "a""#, | ||
618 | ); | ||
619 | } | ||
620 | |||
621 | #[test] | ||
622 | fn test_match_literal() { | ||
623 | parse_macro( | ||
624 | r#" | ||
625 | macro_rules! foo { | ||
626 | ('(') => { | ||
627 | fn foo() {} | ||
628 | } | ||
629 | } | ||
630 | "#, | ||
631 | ) | ||
632 | .assert_expand_items("foo! ['('];", "fn foo () {}"); | ||
633 | } | ||
634 | |||
635 | #[test] | ||
636 | fn test_parse_macro_def_simple() { | ||
637 | cov_mark::check!(parse_macro_def_simple); | ||
638 | |||
639 | parse_macro2( | ||
640 | r#" | ||
641 | macro foo($id:ident) { | ||
642 | fn $id() {} | ||
643 | } | ||
644 | "#, | ||
645 | ) | ||
646 | .assert_expand_items("foo!(bar);", "fn bar () {}"); | ||
647 | } | ||
648 | |||
649 | #[test] | ||
650 | fn test_parse_macro_def_rules() { | ||
651 | cov_mark::check!(parse_macro_def_rules); | ||
652 | |||
653 | parse_macro2( | ||
654 | r#" | ||
655 | macro foo { | ||
656 | ($id:ident) => { | ||
657 | fn $id() {} | ||
658 | } | ||
659 | } | ||
660 | "#, | ||
661 | ) | ||
662 | .assert_expand_items("foo!(bar);", "fn bar () {}"); | ||
663 | } | ||
664 | |||
665 | #[test] | ||
666 | fn test_path() { | ||
667 | parse_macro( | ||
668 | r#" | ||
669 | macro_rules! foo { | ||
670 | ($ i:path) => { | ||
671 | fn foo() { let a = $ i; } | ||
672 | } | ||
673 | } | ||
674 | "#, | ||
675 | ) | ||
676 | .assert_expand_items("foo! { foo }", "fn foo () {let a = foo ;}") | ||
677 | .assert_expand_items( | ||
678 | "foo! { bar::<u8>::baz::<u8> }", | ||
679 | "fn foo () {let a = bar ::< u8 >:: baz ::< u8 > ;}", | ||
680 | ); | ||
681 | } | ||
682 | |||
683 | #[test] | ||
684 | fn test_two_paths() { | ||
685 | parse_macro( | ||
686 | r#" | ||
687 | macro_rules! foo { | ||
688 | ($ i:path, $ j:path) => { | ||
689 | fn foo() { let a = $ i; let b = $j; } | ||
690 | } | ||
691 | } | ||
692 | "#, | ||
693 | ) | ||
694 | .assert_expand_items("foo! { foo, bar }", "fn foo () {let a = foo ; let b = bar ;}"); | ||
695 | } | ||
696 | |||
697 | #[test] | ||
698 | fn test_path_with_path() { | ||
699 | parse_macro( | ||
700 | r#" | ||
701 | macro_rules! foo { | ||
702 | ($ i:path) => { | ||
703 | fn foo() { let a = $ i :: bar; } | ||
704 | } | ||
705 | } | ||
706 | "#, | ||
707 | ) | ||
708 | .assert_expand_items("foo! { foo }", "fn foo () {let a = foo :: bar ;}"); | ||
709 | } | ||
710 | |||
711 | #[test] | ||
712 | fn test_expr() { | ||
713 | parse_macro( | ||
714 | r#" | ||
715 | macro_rules! foo { | ||
716 | ($ i:expr) => { | ||
717 | fn bar() { $ i; } | ||
718 | } | ||
719 | } | ||
720 | "#, | ||
721 | ) | ||
722 | .assert_expand_items( | ||
723 | "foo! { 2 + 2 * baz(3).quux() }", | ||
724 | "fn bar () {2 + 2 * baz (3) . quux () ;}", | ||
725 | ); | ||
726 | } | ||
727 | |||
728 | #[test] | ||
729 | fn test_last_expr() { | ||
730 | parse_macro( | ||
731 | r#" | ||
732 | macro_rules! vec { | ||
733 | ($($item:expr),*) => { | ||
734 | { | ||
735 | let mut v = Vec::new(); | ||
736 | $( | ||
737 | v.push($item); | ||
738 | )* | ||
739 | v | ||
740 | } | ||
741 | }; | ||
742 | } | ||
743 | "#, | ||
744 | ) | ||
745 | .assert_expand_items( | ||
746 | "vec!(1,2,3);", | ||
747 | "{let mut v = Vec :: new () ; v . push (1) ; v . push (2) ; v . push (3) ; v}", | ||
748 | ); | ||
749 | } | ||
750 | |||
751 | #[test] | ||
752 | fn test_expr_with_attr() { | ||
753 | parse_macro( | ||
754 | r#" | ||
755 | macro_rules! m { | ||
756 | ($a:expr) => {0} | ||
757 | } | ||
758 | "#, | ||
759 | ) | ||
760 | .assert_expand_items("m!(#[allow(a)]())", "0"); | ||
761 | } | ||
762 | |||
763 | #[test] | ||
764 | fn test_ty() { | ||
765 | parse_macro( | ||
766 | r#" | ||
767 | macro_rules! foo { | ||
768 | ($ i:ty) => ( | ||
769 | fn bar() -> $ i { unimplemented!() } | ||
770 | ) | ||
771 | } | ||
772 | "#, | ||
773 | ) | ||
774 | .assert_expand_items("foo! { Baz<u8> }", "fn bar () -> Baz < u8 > {unimplemented ! ()}"); | ||
775 | } | ||
776 | |||
777 | #[test] | ||
778 | fn test_ty_with_complex_type() { | ||
779 | parse_macro( | ||
780 | r#" | ||
781 | macro_rules! foo { | ||
782 | ($ i:ty) => ( | ||
783 | fn bar() -> $ i { unimplemented!() } | ||
784 | ) | ||
785 | } | ||
786 | "#, | ||
787 | ) | ||
788 | // Reference lifetime struct with generic type | ||
789 | .assert_expand_items( | ||
790 | "foo! { &'a Baz<u8> }", | ||
791 | "fn bar () -> & 'a Baz < u8 > {unimplemented ! ()}", | ||
792 | ) | ||
793 | // extern "Rust" func type | ||
794 | .assert_expand_items( | ||
795 | r#"foo! { extern "Rust" fn() -> Ret }"#, | ||
796 | r#"fn bar () -> extern "Rust" fn () -> Ret {unimplemented ! ()}"#, | ||
797 | ); | ||
798 | } | ||
799 | |||
800 | #[test] | ||
801 | fn test_pat_() { | ||
802 | parse_macro( | ||
803 | r#" | ||
804 | macro_rules! foo { | ||
805 | ($ i:pat) => { fn foo() { let $ i; } } | ||
806 | } | ||
807 | "#, | ||
808 | ) | ||
809 | .assert_expand_items("foo! { (a, b) }", "fn foo () {let (a , b) ;}"); | ||
810 | } | ||
811 | |||
812 | #[test] | ||
813 | fn test_stmt() { | ||
814 | parse_macro( | ||
815 | r#" | ||
816 | macro_rules! foo { | ||
817 | ($ i:stmt) => ( | ||
818 | fn bar() { $ i; } | ||
819 | ) | ||
820 | } | ||
821 | "#, | ||
822 | ) | ||
823 | .assert_expand_items("foo! { 2 }", "fn bar () {2 ;}") | ||
824 | .assert_expand_items("foo! { let a = 0 }", "fn bar () {let a = 0 ;}"); | ||
825 | } | ||
826 | |||
827 | #[test] | ||
828 | fn test_single_item() { | ||
829 | parse_macro( | ||
830 | r#" | ||
831 | macro_rules! foo { | ||
832 | ($ i:item) => ( | ||
833 | $ i | ||
834 | ) | ||
835 | } | ||
836 | "#, | ||
837 | ) | ||
838 | .assert_expand_items("foo! {mod c {}}", "mod c {}"); | ||
839 | } | ||
840 | |||
841 | #[test] | ||
842 | fn test_all_items() { | ||
843 | parse_macro( | ||
844 | r#" | ||
845 | macro_rules! foo { | ||
846 | ($ ($ i:item)*) => ($ ( | ||
847 | $ i | ||
848 | )*) | ||
849 | } | ||
850 | "#, | ||
851 | ). | ||
852 | assert_expand_items( | ||
853 | r#" | ||
854 | foo! { | ||
855 | extern crate a; | ||
856 | mod b; | ||
857 | mod c {} | ||
858 | use d; | ||
859 | const E: i32 = 0; | ||
860 | static F: i32 = 0; | ||
861 | impl G {} | ||
862 | struct H; | ||
863 | enum I { Foo } | ||
864 | trait J {} | ||
865 | fn h() {} | ||
866 | extern {} | ||
867 | type T = u8; | ||
868 | } | ||
869 | "#, | ||
870 | r#"extern crate a ; mod b ; mod c {} use d ; const E : i32 = 0 ; static F : i32 = 0 ; impl G {} struct H ; enum I {Foo} trait J {} fn h () {} extern {} type T = u8 ;"#, | ||
871 | ); | ||
872 | } | ||
873 | |||
874 | #[test] | ||
875 | fn test_block() { | ||
876 | parse_macro( | ||
877 | r#" | ||
878 | macro_rules! foo { | ||
879 | ($ i:block) => { fn foo() $ i } | ||
880 | } | ||
881 | "#, | ||
882 | ) | ||
883 | .assert_expand_statements("foo! { { 1; } }", "fn foo () {1 ;}"); | ||
884 | } | ||
885 | |||
886 | #[test] | ||
887 | fn test_meta() { | ||
888 | parse_macro( | ||
889 | r#" | ||
890 | macro_rules! foo { | ||
891 | ($ i:meta) => ( | ||
892 | #[$ i] | ||
893 | fn bar() {} | ||
894 | ) | ||
895 | } | ||
896 | "#, | ||
897 | ) | ||
898 | .assert_expand_items( | ||
899 | r#"foo! { cfg(target_os = "windows") }"#, | ||
900 | r#"# [cfg (target_os = "windows")] fn bar () {}"#, | ||
901 | ) | ||
902 | .assert_expand_items(r#"foo! { hello::world }"#, r#"# [hello :: world] fn bar () {}"#); | ||
903 | } | ||
904 | |||
905 | #[test] | ||
906 | fn test_meta_doc_comments() { | ||
907 | parse_macro( | ||
908 | r#" | ||
909 | macro_rules! foo { | ||
910 | ($(#[$ i:meta])+) => ( | ||
911 | $(#[$ i])+ | ||
912 | fn bar() {} | ||
913 | ) | ||
914 | } | ||
915 | "#, | ||
916 | ). | ||
917 | assert_expand_items( | ||
918 | r#"foo! { | ||
919 | /// Single Line Doc 1 | ||
920 | /** | ||
921 | MultiLines Doc | ||
922 | */ | ||
923 | }"#, | ||
924 | "# [doc = \" Single Line Doc 1\"] # [doc = \"\\\\n MultiLines Doc\\\\n \"] fn bar () {}", | ||
925 | ); | ||
926 | } | ||
927 | |||
928 | #[test] | ||
929 | fn test_meta_doc_comments_non_latin() { | ||
930 | parse_macro( | ||
931 | r#" | ||
932 | macro_rules! foo { | ||
933 | ($(#[$ i:meta])+) => ( | ||
934 | $(#[$ i])+ | ||
935 | fn bar() {} | ||
936 | ) | ||
937 | } | ||
938 | "#, | ||
939 | ). | ||
940 | assert_expand_items( | ||
941 | r#"foo! { | ||
942 | /// 錦瑟無端五十弦,一弦一柱思華年。 | ||
943 | /** | ||
944 | 莊生曉夢迷蝴蝶,望帝春心託杜鵑。 | ||
945 | */ | ||
946 | }"#, | ||
947 | "# [doc = \" 錦瑟無端五十弦,一弦一柱思華年。\"] # [doc = \"\\\\n 莊生曉夢迷蝴蝶,望帝春心託杜鵑。\\\\n \"] fn bar () {}", | ||
948 | ); | ||
949 | } | ||
950 | |||
951 | #[test] | ||
952 | fn test_tt_block() { | ||
953 | parse_macro( | ||
954 | r#" | ||
955 | macro_rules! foo { | ||
956 | ($ i:tt) => { fn foo() $ i } | ||
957 | } | ||
958 | "#, | ||
959 | ) | ||
960 | .assert_expand_items(r#"foo! { { 1; } }"#, r#"fn foo () {1 ;}"#); | ||
961 | } | ||
962 | |||
963 | #[test] | ||
964 | fn test_tt_group() { | ||
965 | parse_macro( | ||
966 | r#" | ||
967 | macro_rules! foo { | ||
968 | ($($ i:tt)*) => { $($ i)* } | ||
969 | } | ||
970 | "#, | ||
971 | ) | ||
972 | .assert_expand_items(r#"foo! { fn foo() {} }"#, r#"fn foo () {}"#); | ||
973 | } | ||
974 | |||
975 | #[test] | ||
976 | fn test_tt_composite() { | ||
977 | parse_macro( | ||
978 | r#" | ||
979 | macro_rules! foo { | ||
980 | ($i:tt) => { 0 } | ||
981 | } | ||
982 | "#, | ||
983 | ) | ||
984 | .assert_expand_items(r#"foo! { => }"#, r#"0"#); | ||
985 | } | ||
986 | |||
987 | #[test] | ||
988 | fn test_tt_composite2() { | ||
989 | let node = parse_macro( | ||
990 | r#" | ||
991 | macro_rules! foo { | ||
992 | ($($tt:tt)*) => { abs!(=> $($tt)*) } | ||
993 | } | ||
994 | "#, | ||
995 | ) | ||
996 | .expand_items(r#"foo!{#}"#); | ||
997 | |||
998 | let res = format!("{:#?}", &node); | ||
999 | assert_eq_text!( | ||
1000 | r###"[email protected] | ||
1001 | [email protected] | ||
1002 | [email protected] | ||
1003 | [email protected] | ||
1004 | [email protected] | ||
1005 | [email protected] "abs" | ||
1006 | [email protected] "!" | ||
1007 | [email protected] | ||
1008 | [email protected] "(" | ||
1009 | [email protected] "=" | ||
1010 | [email protected] ">" | ||
1011 | [email protected] " " | ||
1012 | [email protected] "#" | ||
1013 | [email protected] ")""###, | ||
1014 | res.trim() | ||
1015 | ); | ||
1016 | } | ||
1017 | |||
1018 | #[test] | ||
1019 | fn test_tt_with_composite_without_space() { | ||
1020 | parse_macro( | ||
1021 | r#" | ||
1022 | macro_rules! foo { | ||
1023 | ($ op:tt, $j:path) => ( | ||
1024 | 0 | ||
1025 | ) | ||
1026 | } | ||
1027 | "#, | ||
1028 | ) | ||
1029 | // Test macro input without any spaces | ||
1030 | // See https://github.com/rust-analyzer/rust-analyzer/issues/6692 | ||
1031 | .assert_expand_items("foo!(==,Foo::Bool)", "0"); | ||
1032 | } | ||
1033 | |||
1034 | #[test] | ||
1035 | fn test_underscore() { | ||
1036 | parse_macro( | ||
1037 | r#" | ||
1038 | macro_rules! foo { | ||
1039 | ($_:tt) => { 0 } | ||
1040 | } | ||
1041 | "#, | ||
1042 | ) | ||
1043 | .assert_expand_items(r#"foo! { => }"#, r#"0"#); | ||
1044 | } | ||
1045 | |||
1046 | #[test] | ||
1047 | fn test_underscore_not_greedily() { | ||
1048 | parse_macro( | ||
1049 | r#" | ||
1050 | macro_rules! q { | ||
1051 | ($($a:ident)* _) => {0}; | ||
1052 | } | ||
1053 | "#, | ||
1054 | ) | ||
1055 | // `_` overlaps with `$a:ident` but rustc matches it under the `_` token | ||
1056 | .assert_expand_items(r#"q![a b c d _]"#, r#"0"#); | ||
1057 | |||
1058 | parse_macro( | ||
1059 | r#" | ||
1060 | macro_rules! q { | ||
1061 | ($($a:expr => $b:ident)* _ => $c:expr) => {0}; | ||
1062 | } | ||
1063 | "#, | ||
1064 | ) | ||
1065 | // `_ => ou` overlaps with `$a:expr => $b:ident` but rustc matches it under `_ => $c:expr` | ||
1066 | .assert_expand_items(r#"q![a => b c => d _ => ou]"#, r#"0"#); | ||
1067 | } | ||
1068 | |||
1069 | #[test] | ||
1070 | fn test_underscore_as_type() { | ||
1071 | parse_macro( | ||
1072 | r#" | ||
1073 | macro_rules! q { | ||
1074 | ($a:ty) => {0}; | ||
1075 | } | ||
1076 | "#, | ||
1077 | ) | ||
1078 | // Underscore is a type | ||
1079 | .assert_expand_items(r#"q![_]"#, r#"0"#); | ||
1080 | } | ||
1081 | |||
1082 | #[test] | ||
1083 | fn test_vertical_bar_with_pat() { | ||
1084 | parse_macro( | ||
1085 | r#" | ||
1086 | macro_rules! foo { | ||
1087 | (| $pat:pat | ) => { 0 } | ||
1088 | } | ||
1089 | "#, | ||
1090 | ) | ||
1091 | .assert_expand_items(r#"foo! { | x | }"#, r#"0"#); | ||
1092 | } | ||
1093 | |||
1094 | #[test] | ||
1095 | fn test_dollar_crate_lhs_is_not_meta() { | ||
1096 | parse_macro( | ||
1097 | r#" | ||
1098 | macro_rules! foo { | ||
1099 | ($crate) => {}; | ||
1100 | () => {0}; | ||
1101 | } | ||
1102 | "#, | ||
1103 | ) | ||
1104 | .assert_expand_items(r#"foo!{}"#, r#"0"#); | ||
1105 | } | ||
1106 | |||
1107 | #[test] | ||
1108 | fn test_lifetime() { | ||
1109 | parse_macro( | ||
1110 | r#" | ||
1111 | macro_rules! foo { | ||
1112 | ($ lt:lifetime) => { struct Ref<$ lt>{ s: &$ lt str } } | ||
1113 | } | ||
1114 | "#, | ||
1115 | ) | ||
1116 | .assert_expand_items(r#"foo!{'a}"#, r#"struct Ref <'a > {s : &'a str}"#); | ||
1117 | } | ||
1118 | |||
1119 | #[test] | ||
1120 | fn test_literal() { | ||
1121 | parse_macro( | ||
1122 | r#" | ||
1123 | macro_rules! foo { | ||
1124 | ($ type:ty , $ lit:literal) => { const VALUE: $ type = $ lit;}; | ||
1125 | } | ||
1126 | "#, | ||
1127 | ) | ||
1128 | .assert_expand_items(r#"foo!(u8,0);"#, r#"const VALUE : u8 = 0 ;"#); | ||
1129 | |||
1130 | parse_macro( | ||
1131 | r#" | ||
1132 | macro_rules! foo { | ||
1133 | ($ type:ty , $ lit:literal) => { const VALUE: $ type = $ lit;}; | ||
1134 | } | ||
1135 | "#, | ||
1136 | ) | ||
1137 | .assert_expand_items(r#"foo!(i32,-1);"#, r#"const VALUE : i32 = - 1 ;"#); | ||
1138 | } | ||
1139 | |||
1140 | #[test] | ||
1141 | fn test_boolean_is_ident() { | ||
1142 | parse_macro( | ||
1143 | r#" | ||
1144 | macro_rules! foo { | ||
1145 | ($lit0:literal, $lit1:literal) => { const VALUE: (bool,bool) = ($lit0,$lit1); }; | ||
1146 | } | ||
1147 | "#, | ||
1148 | ) | ||
1149 | .assert_expand( | ||
1150 | r#"foo!(true,false);"#, | ||
1151 | r#" | ||
1152 | SUBTREE $ | ||
1153 | IDENT const 14 | ||
1154 | IDENT VALUE 15 | ||
1155 | PUNCH : [alone] 16 | ||
1156 | SUBTREE () 17 | ||
1157 | IDENT bool 18 | ||
1158 | PUNCH , [alone] 19 | ||
1159 | IDENT bool 20 | ||
1160 | PUNCH = [alone] 21 | ||
1161 | SUBTREE () 22 | ||
1162 | IDENT true 29 | ||
1163 | PUNCH , [joint] 25 | ||
1164 | IDENT false 31 | ||
1165 | PUNCH ; [alone] 28 | ||
1166 | "#, | ||
1167 | ); | ||
1168 | } | ||
1169 | |||
1170 | #[test] | ||
1171 | fn test_vis() { | ||
1172 | parse_macro( | ||
1173 | r#" | ||
1174 | macro_rules! foo { | ||
1175 | ($ vis:vis $ name:ident) => { $ vis fn $ name() {}}; | ||
1176 | } | ||
1177 | "#, | ||
1178 | ) | ||
1179 | .assert_expand_items(r#"foo!(pub foo);"#, r#"pub fn foo () {}"#) | ||
1180 | // test optional cases | ||
1181 | .assert_expand_items(r#"foo!(foo);"#, r#"fn foo () {}"#); | ||
1182 | } | ||
1183 | |||
1184 | #[test] | ||
1185 | fn test_inner_macro_rules() { | ||
1186 | parse_macro( | ||
1187 | r#" | ||
1188 | macro_rules! foo { | ||
1189 | ($a:ident, $b:ident, $c:tt) => { | ||
1190 | |||
1191 | macro_rules! bar { | ||
1192 | ($bi:ident) => { | ||
1193 | fn $bi() -> u8 {$c} | ||
1194 | } | ||
1195 | } | ||
1196 | |||
1197 | bar!($a); | ||
1198 | fn $b() -> u8 {$c} | ||
1199 | } | ||
1200 | } | ||
1201 | "#, | ||
1202 | ). | ||
1203 | assert_expand_items( | ||
1204 | r#"foo!(x,y, 1);"#, | ||
1205 | r#"macro_rules ! bar {($ bi : ident) => {fn $ bi () -> u8 {1}}} bar ! (x) ; fn y () -> u8 {1}"#, | ||
1206 | ); | ||
1207 | } | ||
1208 | |||
1209 | #[test] | ||
1210 | fn test_expr_after_path_colons() { | ||
1211 | assert!(parse_macro( | ||
1212 | r#" | ||
1213 | macro_rules! m { | ||
1214 | ($k:expr) => { | ||
1215 | f(K::$k); | ||
1216 | } | ||
1217 | } | ||
1218 | "#, | ||
1219 | ) | ||
1220 | .expand_statements(r#"m!(C("0"))"#) | ||
1221 | .descendants() | ||
1222 | .find(|token| token.kind() == ERROR) | ||
1223 | .is_some()); | ||
1224 | } | ||
1225 | |||
1226 | #[test] | ||
1227 | fn test_match_is_not_greedy() { | ||
1228 | parse_macro( | ||
1229 | r#" | ||
1230 | macro_rules! foo { | ||
1231 | ($($i:ident $(,)*),*) => {}; | ||
1232 | } | ||
1233 | "#, | ||
1234 | ) | ||
1235 | .assert_expand_items(r#"foo!(a,b);"#, r#""#); | ||
1236 | } | ||
1237 | |||
1238 | // The following tests are based on real world situations | ||
1239 | #[test] | ||
1240 | fn test_vec() { | ||
1241 | let fixture = parse_macro( | ||
1242 | r#" | ||
1243 | macro_rules! vec { | ||
1244 | ($($item:expr),*) => { | ||
1245 | { | ||
1246 | let mut v = Vec::new(); | ||
1247 | $( | ||
1248 | v.push($item); | ||
1249 | )* | ||
1250 | v | ||
1251 | } | ||
1252 | }; | ||
1253 | } | ||
1254 | "#, | ||
1255 | ); | ||
1256 | fixture | ||
1257 | .assert_expand_items(r#"vec!();"#, r#"{let mut v = Vec :: new () ; v}"#) | ||
1258 | .assert_expand_items( | ||
1259 | r#"vec![1u32,2];"#, | ||
1260 | r#"{let mut v = Vec :: new () ; v . push (1u32) ; v . push (2) ; v}"#, | ||
1261 | ); | ||
1262 | |||
1263 | let tree = fixture.expand_expr(r#"vec![1u32,2];"#); | ||
1264 | |||
1265 | assert_eq!( | ||
1266 | format!("{:#?}", tree).trim(), | ||
1267 | r#"[email protected] | ||
1268 | [email protected] "{" | ||
1269 | [email protected] | ||
1270 | [email protected] "let" | ||
1271 | [email protected] | ||
1272 | [email protected] "mut" | ||
1273 | [email protected] | ||
1274 | [email protected] "v" | ||
1275 | [email protected] "=" | ||
1276 | [email protected] | ||
1277 | [email protected] | ||
1278 | [email protected] | ||
1279 | [email protected] | ||
1280 | [email protected] | ||
1281 | [email protected] | ||
1282 | [email protected] "Vec" | ||
1283 | [email protected] "::" | ||
1284 | [email protected] | ||
1285 | [email protected] | ||
1286 | [email protected] "new" | ||
1287 | [email protected] | ||
1288 | [email protected] "(" | ||
1289 | [email protected] ")" | ||
1290 | [email protected] ";" | ||
1291 | [email protected] | ||
1292 | [email protected] | ||
1293 | [email protected] | ||
1294 | [email protected] | ||
1295 | [email protected] | ||
1296 | [email protected] | ||
1297 | [email protected] "v" | ||
1298 | [email protected] "." | ||
1299 | [email protected] | ||
1300 | [email protected] "push" | ||
1301 | [email protected] | ||
1302 | [email protected] "(" | ||
1303 | [email protected] | ||
1304 | [email protected] "1u32" | ||
1305 | [email protected] ")" | ||
1306 | [email protected] ";" | ||
1307 | [email protected] | ||
1308 | [email protected] | ||
1309 | [email protected] | ||
1310 | [email protected] | ||
1311 | [email protected] | ||
1312 | [email protected] | ||
1313 | [email protected] "v" | ||
1314 | [email protected] "." | ||
1315 | [email protected] | ||
1316 | [email protected] "push" | ||
1317 | [email protected] | ||
1318 | [email protected] "(" | ||
1319 | [email protected] | ||
1320 | [email protected] "2" | ||
1321 | [email protected] ")" | ||
1322 | [email protected] ";" | ||
1323 | [email protected] | ||
1324 | [email protected] | ||
1325 | [email protected] | ||
1326 | [email protected] | ||
1327 | [email protected] "v" | ||
1328 | [email protected] "}""# | ||
1329 | ); | ||
1330 | } | ||
1331 | |||
1332 | #[test] | ||
1333 | fn test_winapi_struct() { | ||
1334 | // from https://github.com/retep998/winapi-rs/blob/a7ef2bca086aae76cf6c4ce4c2552988ed9798ad/src/macros.rs#L366 | ||
1335 | |||
1336 | parse_macro( | ||
1337 | r#" | ||
1338 | macro_rules! STRUCT { | ||
1339 | ($(#[$attrs:meta])* struct $name:ident { | ||
1340 | $($field:ident: $ftype:ty,)+ | ||
1341 | }) => ( | ||
1342 | #[repr(C)] #[derive(Copy)] $(#[$attrs])* | ||
1343 | pub struct $name { | ||
1344 | $(pub $field: $ftype,)+ | ||
1345 | } | ||
1346 | impl Clone for $name { | ||
1347 | #[inline] | ||
1348 | fn clone(&self) -> $name { *self } | ||
1349 | } | ||
1350 | #[cfg(feature = "impl-default")] | ||
1351 | impl Default for $name { | ||
1352 | #[inline] | ||
1353 | fn default() -> $name { unsafe { $crate::_core::mem::zeroed() } } | ||
1354 | } | ||
1355 | ); | ||
1356 | } | ||
1357 | "#, | ||
1358 | ). | ||
1359 | // from https://github.com/retep998/winapi-rs/blob/a7ef2bca086aae76cf6c4ce4c2552988ed9798ad/src/shared/d3d9caps.rs | ||
1360 | assert_expand_items(r#"STRUCT!{struct D3DVSHADERCAPS2_0 {Caps: u8,}}"#, | ||
1361 | "# [repr (C)] # [derive (Copy)] pub struct D3DVSHADERCAPS2_0 {pub Caps : u8 ,} impl Clone for D3DVSHADERCAPS2_0 {# [inline] fn clone (& self) -> D3DVSHADERCAPS2_0 {* self}} # [cfg (feature = \"impl-default\")] impl Default for D3DVSHADERCAPS2_0 {# [inline] fn default () -> D3DVSHADERCAPS2_0 {unsafe {$crate :: _core :: mem :: zeroed ()}}}" | ||
1362 | ) | ||
1363 | .assert_expand_items(r#"STRUCT!{#[cfg_attr(target_arch = "x86", repr(packed))] struct D3DCONTENTPROTECTIONCAPS {Caps : u8 ,}}"#, | ||
1364 | "# [repr (C)] # [derive (Copy)] # [cfg_attr (target_arch = \"x86\" , repr (packed))] pub struct D3DCONTENTPROTECTIONCAPS {pub Caps : u8 ,} impl Clone for D3DCONTENTPROTECTIONCAPS {# [inline] fn clone (& self) -> D3DCONTENTPROTECTIONCAPS {* self}} # [cfg (feature = \"impl-default\")] impl Default for D3DCONTENTPROTECTIONCAPS {# [inline] fn default () -> D3DCONTENTPROTECTIONCAPS {unsafe {$crate :: _core :: mem :: zeroed ()}}}" | ||
1365 | ); | ||
1366 | } | ||
1367 | |||
1368 | #[test] | ||
1369 | fn test_int_base() { | ||
1370 | parse_macro( | ||
1371 | r#" | ||
1372 | macro_rules! int_base { | ||
1373 | ($Trait:ident for $T:ident as $U:ident -> $Radix:ident) => { | ||
1374 | #[stable(feature = "rust1", since = "1.0.0")] | ||
1375 | impl fmt::$Trait for $T { | ||
1376 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
1377 | $Radix.fmt_int(*self as $U, f) | ||
1378 | } | ||
1379 | } | ||
1380 | } | ||
1381 | } | ||
1382 | "#, | ||
1383 | ).assert_expand_items(r#" int_base!{Binary for isize as usize -> Binary}"#, | ||
1384 | "# [stable (feature = \"rust1\" , since = \"1.0.0\")] impl fmt ::Binary for isize {fn fmt (& self , f : & mut fmt :: Formatter < \'_ >) -> fmt :: Result {Binary . fmt_int (* self as usize , f)}}" | ||
1385 | ); | ||
1386 | } | ||
1387 | |||
1388 | #[test] | ||
1389 | fn test_generate_pattern_iterators() { | ||
1390 | // from https://github.com/rust-lang/rust/blob/316a391dcb7d66dc25f1f9a4ec9d368ef7615005/src/libcore/str/mod.rs | ||
1391 | parse_macro( | ||
1392 | r#" | ||
1393 | macro_rules! generate_pattern_iterators { | ||
1394 | { double ended; with $(#[$common_stability_attribute:meta])*, | ||
1395 | $forward_iterator:ident, | ||
1396 | $reverse_iterator:ident, $iterty:ty | ||
1397 | } => { | ||
1398 | fn foo(){} | ||
1399 | } | ||
1400 | } | ||
1401 | "#, | ||
1402 | ).assert_expand_items( | ||
1403 | r#"generate_pattern_iterators ! ( double ended ; with # [ stable ( feature = "rust1" , since = "1.0.0" ) ] , Split , RSplit , & 'a str );"#, | ||
1404 | "fn foo () {}", | ||
1405 | ); | ||
1406 | } | ||
1407 | |||
1408 | #[test] | ||
1409 | fn test_impl_fn_for_zst() { | ||
1410 | // from https://github.com/rust-lang/rust/blob/5d20ff4d2718c820632b38c1e49d4de648a9810b/src/libcore/internal_macros.rs | ||
1411 | parse_macro( | ||
1412 | r#" | ||
1413 | macro_rules! impl_fn_for_zst { | ||
1414 | { $( $( #[$attr: meta] )* | ||
1415 | struct $Name: ident impl$( <$( $lifetime : lifetime ),+> )? Fn = | ||
1416 | |$( $arg: ident: $ArgTy: ty ),*| -> $ReturnTy: ty | ||
1417 | $body: block; )+ | ||
1418 | } => { | ||
1419 | $( | ||
1420 | $( #[$attr] )* | ||
1421 | struct $Name; | ||
1422 | |||
1423 | impl $( <$( $lifetime ),+> )? Fn<($( $ArgTy, )*)> for $Name { | ||
1424 | #[inline] | ||
1425 | extern "rust-call" fn call(&self, ($( $arg, )*): ($( $ArgTy, )*)) -> $ReturnTy { | ||
1426 | $body | ||
1427 | } | ||
1428 | } | ||
1429 | |||
1430 | impl $( <$( $lifetime ),+> )? FnMut<($( $ArgTy, )*)> for $Name { | ||
1431 | #[inline] | ||
1432 | extern "rust-call" fn call_mut( | ||
1433 | &mut self, | ||
1434 | ($( $arg, )*): ($( $ArgTy, )*) | ||
1435 | ) -> $ReturnTy { | ||
1436 | Fn::call(&*self, ($( $arg, )*)) | ||
1437 | } | ||
1438 | } | ||
1439 | |||
1440 | impl $( <$( $lifetime ),+> )? FnOnce<($( $ArgTy, )*)> for $Name { | ||
1441 | type Output = $ReturnTy; | ||
1442 | |||
1443 | #[inline] | ||
1444 | extern "rust-call" fn call_once(self, ($( $arg, )*): ($( $ArgTy, )*)) -> $ReturnTy { | ||
1445 | Fn::call(&self, ($( $arg, )*)) | ||
1446 | } | ||
1447 | } | ||
1448 | )+ | ||
1449 | } | ||
1450 | } | ||
1451 | "#, | ||
1452 | ).assert_expand_items(r#" | ||
1453 | impl_fn_for_zst ! { | ||
1454 | # [ derive ( Clone ) ] | ||
1455 | struct CharEscapeDebugContinue impl Fn = | c : char | -> char :: EscapeDebug { | ||
1456 | c . escape_debug_ext ( false ) | ||
1457 | } ; | ||
1458 | |||
1459 | # [ derive ( Clone ) ] | ||
1460 | struct CharEscapeUnicode impl Fn = | c : char | -> char :: EscapeUnicode { | ||
1461 | c . escape_unicode ( ) | ||
1462 | } ; | ||
1463 | # [ derive ( Clone ) ] | ||
1464 | struct CharEscapeDefault impl Fn = | c : char | -> char :: EscapeDefault { | ||
1465 | c . escape_default ( ) | ||
1466 | } ; | ||
1467 | } | ||
1468 | "#, | ||
1469 | "# [derive (Clone)] struct CharEscapeDebugContinue ; impl Fn < (char ,) > for CharEscapeDebugContinue {# [inline] extern \"rust-call\" fn call (& self , (c ,) : (char ,)) -> char :: EscapeDebug {{c . escape_debug_ext (false)}}} impl FnMut < (char ,) > for CharEscapeDebugContinue {# [inline] extern \"rust-call\" fn call_mut (& mut self , (c ,) : (char ,)) -> char :: EscapeDebug {Fn :: call (&* self , (c ,))}} impl FnOnce < (char ,) > for CharEscapeDebugContinue {type Output = char :: EscapeDebug ; # [inline] extern \"rust-call\" fn call_once (self , (c ,) : (char ,)) -> char :: EscapeDebug {Fn :: call (& self , (c ,))}} # [derive (Clone)] struct CharEscapeUnicode ; impl Fn < (char ,) > for CharEscapeUnicode {# [inline] extern \"rust-call\" fn call (& self , (c ,) : (char ,)) -> char :: EscapeUnicode {{c . escape_unicode ()}}} impl FnMut < (char ,) > for CharEscapeUnicode {# [inline] extern \"rust-call\" fn call_mut (& mut self , (c ,) : (char ,)) -> char :: EscapeUnicode {Fn :: call (&* self , (c ,))}} impl FnOnce < (char ,) > for CharEscapeUnicode {type Output = char :: EscapeUnicode ; # [inline] extern \"rust-call\" fn call_once (self , (c ,) : (char ,)) -> char :: EscapeUnicode {Fn :: call (& self , (c ,))}} # [derive (Clone)] struct CharEscapeDefault ; impl Fn < (char ,) > for CharEscapeDefault {# [inline] extern \"rust-call\" fn call (& self , (c ,) : (char ,)) -> char :: EscapeDefault {{c . escape_default ()}}} impl FnMut < (char ,) > for CharEscapeDefault {# [inline] extern \"rust-call\" fn call_mut (& mut self , (c ,) : (char ,)) -> char :: EscapeDefault {Fn :: call (&* self , (c ,))}} impl FnOnce < (char ,) > for CharEscapeDefault {type Output = char :: EscapeDefault ; # [inline] extern \"rust-call\" fn call_once (self , (c ,) : (char ,)) -> char :: EscapeDefault {Fn :: call (& self , (c ,))}}" | ||
1470 | ); | ||
1471 | } | ||
1472 | |||
1473 | #[test] | ||
1474 | fn test_impl_nonzero_fmt() { | ||
1475 | // from https://github.com/rust-lang/rust/blob/316a391dcb7d66dc25f1f9a4ec9d368ef7615005/src/libcore/num/mod.rs#L12 | ||
1476 | parse_macro( | ||
1477 | r#" | ||
1478 | macro_rules! impl_nonzero_fmt { | ||
1479 | ( #[$stability: meta] ( $( $Trait: ident ),+ ) for $Ty: ident ) => { | ||
1480 | fn foo () {} | ||
1481 | } | ||
1482 | } | ||
1483 | "#, | ||
1484 | ).assert_expand_items( | ||
1485 | r#"impl_nonzero_fmt! { # [stable(feature= "nonzero",since="1.28.0")] (Debug,Display,Binary,Octal,LowerHex,UpperHex) for NonZeroU8}"#, | ||
1486 | "fn foo () {}", | ||
1487 | ); | ||
1488 | } | ||
1489 | |||
1490 | #[test] | ||
1491 | fn test_cfg_if_items() { | ||
1492 | // from https://github.com/rust-lang/rust/blob/33fe1131cadba69d317156847be9a402b89f11bb/src/libstd/macros.rs#L986 | ||
1493 | parse_macro( | ||
1494 | r#" | ||
1495 | macro_rules! __cfg_if_items { | ||
1496 | (($($not:meta,)*) ; ) => {}; | ||
1497 | (($($not:meta,)*) ; ( ($($m:meta),*) ($($it:item)*) ), $($rest:tt)*) => { | ||
1498 | __cfg_if_items! { ($($not,)* $($m,)*) ; $($rest)* } | ||
1499 | } | ||
1500 | } | ||
1501 | "#, | ||
1502 | ).assert_expand_items( | ||
1503 | r#"__cfg_if_items ! { ( rustdoc , ) ; ( ( ) ( # [ cfg ( any ( target_os = "redox" , unix ) ) ] # [ stable ( feature = "rust1" , since = "1.0.0" ) ] pub use sys :: ext as unix ; # [ cfg ( windows ) ] # [ stable ( feature = "rust1" , since = "1.0.0" ) ] pub use sys :: ext as windows ; # [ cfg ( any ( target_os = "linux" , target_os = "l4re" ) ) ] pub mod linux ; ) ) , }"#, | ||
1504 | "__cfg_if_items ! {(rustdoc ,) ;}", | ||
1505 | ); | ||
1506 | } | ||
1507 | |||
1508 | #[test] | ||
1509 | fn test_cfg_if_main() { | ||
1510 | // from https://github.com/rust-lang/rust/blob/3d211248393686e0f73851fc7548f6605220fbe1/src/libpanic_unwind/macros.rs#L9 | ||
1511 | parse_macro( | ||
1512 | r#" | ||
1513 | macro_rules! cfg_if { | ||
1514 | ($( | ||
1515 | if #[cfg($($meta:meta),*)] { $($it:item)* } | ||
1516 | ) else * else { | ||
1517 | $($it2:item)* | ||
1518 | }) => { | ||
1519 | __cfg_if_items! { | ||
1520 | () ; | ||
1521 | $( ( ($($meta),*) ($($it)*) ), )* | ||
1522 | ( () ($($it2)*) ), | ||
1523 | } | ||
1524 | }; | ||
1525 | |||
1526 | // Internal macro to Apply a cfg attribute to a list of items | ||
1527 | (@__apply $m:meta, $($it:item)*) => { | ||
1528 | $(#[$m] $it)* | ||
1529 | }; | ||
1530 | } | ||
1531 | "#, | ||
1532 | ).assert_expand_items(r#" | ||
1533 | cfg_if ! { | ||
1534 | if # [ cfg ( target_env = "msvc" ) ] { | ||
1535 | // no extra unwinder support needed | ||
1536 | } else if # [ cfg ( all ( target_arch = "wasm32" , not ( target_os = "emscripten" ) ) ) ] { | ||
1537 | // no unwinder on the system! | ||
1538 | } else { | ||
1539 | mod libunwind ; | ||
1540 | pub use libunwind :: * ; | ||
1541 | } | ||
1542 | } | ||
1543 | "#, | ||
1544 | "__cfg_if_items ! {() ; ((target_env = \"msvc\") ()) , ((all (target_arch = \"wasm32\" , not (target_os = \"emscripten\"))) ()) , (() (mod libunwind ; pub use libunwind :: * ;)) ,}" | ||
1545 | ).assert_expand_items( | ||
1546 | r#" | ||
1547 | cfg_if ! { @ __apply cfg ( all ( not ( any ( not ( any ( target_os = "solaris" , target_os = "illumos" ) ) ) ) ) ) , } | ||
1548 | "#, | ||
1549 | "", | ||
1550 | ); | ||
1551 | } | ||
1552 | |||
1553 | #[test] | ||
1554 | fn test_proptest_arbitrary() { | ||
1555 | // from https://github.com/AltSysrq/proptest/blob/d1c4b049337d2f75dd6f49a095115f7c532e5129/proptest/src/arbitrary/macros.rs#L16 | ||
1556 | parse_macro( | ||
1557 | r#" | ||
1558 | macro_rules! arbitrary { | ||
1559 | ([$($bounds : tt)*] $typ: ty, $strat: ty, $params: ty; | ||
1560 | $args: ident => $logic: expr) => { | ||
1561 | impl<$($bounds)*> $crate::arbitrary::Arbitrary for $typ { | ||
1562 | type Parameters = $params; | ||
1563 | type Strategy = $strat; | ||
1564 | fn arbitrary_with($args: Self::Parameters) -> Self::Strategy { | ||
1565 | $logic | ||
1566 | } | ||
1567 | } | ||
1568 | }; | ||
1569 | |||
1570 | }"#, | ||
1571 | ).assert_expand_items(r#"arbitrary ! ( [ A : Arbitrary ] | ||
1572 | Vec < A > , | ||
1573 | VecStrategy < A :: Strategy > , | ||
1574 | RangedParams1 < A :: Parameters > ; | ||
1575 | args => { let product_unpack ! [ range , a ] = args ; vec ( any_with :: < A > ( a ) , range ) } | ||
1576 | ) ;"#, | ||
1577 | "impl <A : Arbitrary > $crate :: arbitrary :: Arbitrary for Vec < A > {type Parameters = RangedParams1 < A :: Parameters > ; type Strategy = VecStrategy < A :: Strategy > ; fn arbitrary_with (args : Self :: Parameters) -> Self :: Strategy {{let product_unpack ! [range , a] = args ; vec (any_with :: < A > (a) , range)}}}" | ||
1578 | ); | ||
1579 | } | ||
1580 | |||
1581 | #[test] | ||
1582 | fn test_old_ridl() { | ||
1583 | // This is from winapi 2.8, which do not have a link from github | ||
1584 | // | ||
1585 | let expanded = parse_macro( | ||
1586 | r#" | ||
1587 | #[macro_export] | ||
1588 | macro_rules! RIDL { | ||
1589 | (interface $interface:ident ($vtbl:ident) : $pinterface:ident ($pvtbl:ident) | ||
1590 | {$( | ||
1591 | fn $method:ident(&mut self $(,$p:ident : $t:ty)*) -> $rtr:ty | ||
1592 | ),+} | ||
1593 | ) => { | ||
1594 | impl $interface { | ||
1595 | $(pub unsafe fn $method(&mut self) -> $rtr { | ||
1596 | ((*self.lpVtbl).$method)(self $(,$p)*) | ||
1597 | })+ | ||
1598 | } | ||
1599 | }; | ||
1600 | }"#, | ||
1601 | ).expand_tt(r#" | ||
1602 | RIDL!{interface ID3D11Asynchronous(ID3D11AsynchronousVtbl): ID3D11DeviceChild(ID3D11DeviceChildVtbl) { | ||
1603 | fn GetDataSize(&mut self) -> UINT | ||
1604 | }}"#); | ||
1605 | |||
1606 | assert_eq!(expanded.to_string(), "impl ID3D11Asynchronous {pub unsafe fn GetDataSize (& mut self) -> UINT {((* self . lpVtbl) .GetDataSize) (self)}}"); | ||
1607 | } | ||
1608 | |||
1609 | #[test] | ||
1610 | fn test_quick_error() { | ||
1611 | let expanded = parse_macro( | ||
1612 | r#" | ||
1613 | macro_rules! quick_error { | ||
1614 | |||
1615 | (SORT [enum $name:ident $( #[$meta:meta] )*] | ||
1616 | items [$($( #[$imeta:meta] )* | ||
1617 | => $iitem:ident: $imode:tt [$( $ivar:ident: $ityp:ty ),*] | ||
1618 | {$( $ifuncs:tt )*} )* ] | ||
1619 | buf [ ] | ||
1620 | queue [ ] | ||
1621 | ) => { | ||
1622 | quick_error!(ENUMINITION [enum $name $( #[$meta] )*] | ||
1623 | body [] | ||
1624 | queue [$( | ||
1625 | $( #[$imeta] )* | ||
1626 | => | ||
1627 | $iitem: $imode [$( $ivar: $ityp ),*] | ||
1628 | )*] | ||
1629 | ); | ||
1630 | }; | ||
1631 | |||
1632 | } | ||
1633 | "#, | ||
1634 | ) | ||
1635 | .expand_tt( | ||
1636 | r#" | ||
1637 | quick_error ! (SORT [enum Wrapped # [derive (Debug)]] items [ | ||
1638 | => One : UNIT [] {} | ||
1639 | => Two : TUPLE [s :String] {display ("two: {}" , s) from ()} | ||
1640 | ] buf [] queue []) ; | ||
1641 | "#, | ||
1642 | ); | ||
1643 | |||
1644 | assert_eq!(expanded.to_string(), "quick_error ! (ENUMINITION [enum Wrapped # [derive (Debug)]] body [] queue [=> One : UNIT [] => Two : TUPLE [s : String]]) ;"); | ||
1645 | } | ||
1646 | |||
1647 | #[test] | ||
1648 | fn test_empty_repeat_vars_in_empty_repeat_vars() { | ||
1649 | parse_macro( | ||
1650 | r#" | ||
1651 | macro_rules! delegate_impl { | ||
1652 | ([$self_type:ident, $self_wrap:ty, $self_map:ident] | ||
1653 | pub trait $name:ident $(: $sup:ident)* $(+ $more_sup:ident)* { | ||
1654 | |||
1655 | $( | ||
1656 | @escape [type $assoc_name_ext:ident] | ||
1657 | )* | ||
1658 | $( | ||
1659 | @section type | ||
1660 | $( | ||
1661 | $(#[$_assoc_attr:meta])* | ||
1662 | type $assoc_name:ident $(: $assoc_bound:ty)*; | ||
1663 | )+ | ||
1664 | )* | ||
1665 | $( | ||
1666 | @section self | ||
1667 | $( | ||
1668 | $(#[$_method_attr:meta])* | ||
1669 | fn $method_name:ident(self $(: $self_selftype:ty)* $(,$marg:ident : $marg_ty:ty)*) -> $mret:ty; | ||
1670 | )+ | ||
1671 | )* | ||
1672 | $( | ||
1673 | @section nodelegate | ||
1674 | $($tail:tt)* | ||
1675 | )* | ||
1676 | }) => { | ||
1677 | impl<> $name for $self_wrap where $self_type: $name { | ||
1678 | $( | ||
1679 | $( | ||
1680 | fn $method_name(self $(: $self_selftype)* $(,$marg: $marg_ty)*) -> $mret { | ||
1681 | $self_map!(self).$method_name($($marg),*) | ||
1682 | } | ||
1683 | )* | ||
1684 | )* | ||
1685 | } | ||
1686 | } | ||
1687 | } | ||
1688 | "#, | ||
1689 | ).assert_expand_items( | ||
1690 | r#"delegate_impl ! {[G , & 'a mut G , deref] pub trait Data : GraphBase {@ section type type NodeWeight ;}}"#, | ||
1691 | "impl <> Data for & \'a mut G where G : Data {}", | ||
1692 | ); | ||
1693 | } | ||
1694 | |||
1695 | #[test] | ||
1696 | fn expr_interpolation() { | ||
1697 | let expanded = parse_macro( | ||
1698 | r#" | ||
1699 | macro_rules! id { | ||
1700 | ($expr:expr) => { | ||
1701 | map($expr) | ||
1702 | } | ||
1703 | } | ||
1704 | "#, | ||
1705 | ) | ||
1706 | .expand_expr("id!(x + foo);"); | ||
1707 | |||
1708 | assert_eq!(expanded.to_string(), "map(x+foo)"); | ||
1709 | } | ||
1710 | |||
1711 | #[test] | ||
1712 | fn test_issue_2520() { | ||
1713 | let macro_fixture = parse_macro( | ||
1714 | r#" | ||
1715 | macro_rules! my_macro { | ||
1716 | { | ||
1717 | ( $( | ||
1718 | $( [] $sname:ident : $stype:ty )? | ||
1719 | $( [$expr:expr] $nname:ident : $ntype:ty )? | ||
1720 | ),* ) | ||
1721 | } => { | ||
1722 | Test { | ||
1723 | $( | ||
1724 | $( $sname, )? | ||
1725 | )* | ||
1726 | } | ||
1727 | }; | ||
1728 | } | ||
1729 | "#, | ||
1730 | ); | ||
1731 | |||
1732 | macro_fixture.assert_expand_items( | ||
1733 | r#"my_macro ! { | ||
1734 | ([] p1 : u32 , [|_| S0K0] s : S0K0 , [] k0 : i32) | ||
1735 | }"#, | ||
1736 | "Test {p1 , k0 ,}", | ||
1737 | ); | ||
1738 | } | ||
1739 | |||
1740 | #[test] | ||
1741 | fn test_issue_3861() { | ||
1742 | let macro_fixture = parse_macro( | ||
1743 | r#" | ||
1744 | macro_rules! rgb_color { | ||
1745 | ($p:expr, $t: ty) => { | ||
1746 | pub fn new() { | ||
1747 | let _ = 0 as $t << $p; | ||
1748 | } | ||
1749 | }; | ||
1750 | } | ||
1751 | "#, | ||
1752 | ); | ||
1753 | |||
1754 | macro_fixture.expand_items(r#"rgb_color!(8 + 8, u32);"#); | ||
1755 | } | ||
1756 | |||
1757 | #[test] | ||
1758 | fn test_repeat_bad_var() { | ||
1759 | // FIXME: the second rule of the macro should be removed and an error about | ||
1760 | // `$( $c )+` raised | ||
1761 | parse_macro( | ||
1762 | r#" | ||
1763 | macro_rules! foo { | ||
1764 | ($( $b:ident )+) => { | ||
1765 | $( $c )+ | ||
1766 | }; | ||
1767 | ($( $b:ident )+) => { | ||
1768 | $( $b )+ | ||
1769 | } | ||
1770 | } | ||
1771 | "#, | ||
1772 | ) | ||
1773 | .assert_expand_items("foo!(b0 b1);", "b0 b1"); | ||
1774 | } | ||
1775 | |||
1776 | #[test] | ||
1777 | fn test_no_space_after_semi_colon() { | ||
1778 | let expanded = parse_macro( | ||
1779 | r#" | ||
1780 | macro_rules! with_std { ($($i:item)*) => ($(#[cfg(feature = "std")]$i)*) } | ||
1781 | "#, | ||
1782 | ) | ||
1783 | .expand_items(r#"with_std! {mod m;mod f;}"#); | ||
1784 | |||
1785 | let dump = format!("{:#?}", expanded); | ||
1786 | assert_eq_text!( | ||
1787 | r###"[email protected] | ||
1788 | [email protected] | ||
1789 | [email protected] | ||
1790 | [email protected] "#" | ||
1791 | [email protected] "[" | ||
1792 | [email protected] | ||
1793 | [email protected] | ||
1794 | [email protected] | ||
1795 | [email protected] "cfg" | ||
1796 | [email protected] | ||
1797 | [email protected] "(" | ||
1798 | [email protected] "feature" | ||
1799 | [email protected] "=" | ||
1800 | [email protected] "\"std\"" | ||
1801 | [email protected] ")" | ||
1802 | [email protected] "]" | ||
1803 | [email protected] "mod" | ||
1804 | [email protected] | ||
1805 | [email protected] "m" | ||
1806 | [email protected] ";" | ||
1807 | [email protected] | ||
1808 | [email protected] | ||
1809 | [email protected] "#" | ||
1810 | [email protected] "[" | ||
1811 | [email protected] | ||
1812 | [email protected] | ||
1813 | [email protected] | ||
1814 | [email protected] "cfg" | ||
1815 | [email protected] | ||
1816 | [email protected] "(" | ||
1817 | [email protected] "feature" | ||
1818 | [email protected] "=" | ||
1819 | [email protected] "\"std\"" | ||
1820 | [email protected] ")" | ||
1821 | [email protected] "]" | ||
1822 | [email protected] "mod" | ||
1823 | [email protected] | ||
1824 | [email protected] "f" | ||
1825 | [email protected] ";""###, | ||
1826 | dump.trim() | ||
1827 | ); | ||
1828 | } | ||
1829 | |||
1830 | // https://github.com/rust-lang/rust/blob/master/src/test/ui/issues/issue-57597.rs | ||
1831 | #[test] | ||
1832 | fn test_rustc_issue_57597() { | ||
1833 | fn test_error(fixture: &str) { | ||
1834 | assert_eq!(parse_macro_error(fixture), ParseError::RepetitionEmptyTokenTree); | ||
1835 | } | ||
1836 | |||
1837 | test_error("macro_rules! foo { ($($($i:ident)?)+) => {}; }"); | ||
1838 | test_error("macro_rules! foo { ($($($i:ident)?)*) => {}; }"); | ||
1839 | test_error("macro_rules! foo { ($($($i:ident)?)?) => {}; }"); | ||
1840 | test_error("macro_rules! foo { ($($($($i:ident)?)?)?) => {}; }"); | ||
1841 | test_error("macro_rules! foo { ($($($($i:ident)*)?)?) => {}; }"); | ||
1842 | test_error("macro_rules! foo { ($($($($i:ident)?)*)?) => {}; }"); | ||
1843 | test_error("macro_rules! foo { ($($($($i:ident)?)?)*) => {}; }"); | ||
1844 | test_error("macro_rules! foo { ($($($($i:ident)*)*)?) => {}; }"); | ||
1845 | test_error("macro_rules! foo { ($($($($i:ident)?)*)*) => {}; }"); | ||
1846 | test_error("macro_rules! foo { ($($($($i:ident)?)*)+) => {}; }"); | ||
1847 | test_error("macro_rules! foo { ($($($($i:ident)+)?)*) => {}; }"); | ||
1848 | test_error("macro_rules! foo { ($($($($i:ident)+)*)?) => {}; }"); | ||
1849 | } | ||
1850 | |||
1851 | #[test] | ||
1852 | fn test_expand_bad_literal() { | ||
1853 | parse_macro( | ||
1854 | r#" | ||
1855 | macro_rules! foo { ($i:literal) => {}; } | ||
1856 | "#, | ||
1857 | ) | ||
1858 | .assert_expand_err(r#"foo!(&k");"#, &ExpandError::BindingError("".into())); | ||
1859 | } | ||
1860 | |||
1861 | #[test] | ||
1862 | fn test_empty_comments() { | ||
1863 | parse_macro( | ||
1864 | r#" | ||
1865 | macro_rules! one_arg_macro { ($fmt:expr) => (); } | ||
1866 | "#, | ||
1867 | ) | ||
1868 | .assert_expand_err( | ||
1869 | r#"one_arg_macro!(/**/)"#, | ||
1870 | &ExpandError::BindingError("expected Expr".into()), | ||
1871 | ); | ||
1872 | } | ||
diff --git a/crates/mbe/src/tests/rule.rs b/crates/mbe/src/tests/rule.rs new file mode 100644 index 000000000..07277966d --- /dev/null +++ b/crates/mbe/src/tests/rule.rs | |||
@@ -0,0 +1,45 @@ | |||
1 | use syntax::{ast, AstNode}; | ||
2 | |||
3 | use crate::ast_to_token_tree; | ||
4 | |||
5 | use super::*; | ||
6 | |||
7 | #[test] | ||
8 | fn test_valid_arms() { | ||
9 | fn check(macro_body: &str) { | ||
10 | let m = parse_macro_arm(macro_body); | ||
11 | m.unwrap(); | ||
12 | } | ||
13 | |||
14 | check("($i:ident) => ()"); | ||
15 | check("($($i:ident)*) => ($_)"); | ||
16 | check("($($true:ident)*) => ($true)"); | ||
17 | check("($($false:ident)*) => ($false)"); | ||
18 | check("($) => ($)"); | ||
19 | } | ||
20 | |||
21 | #[test] | ||
22 | fn test_invalid_arms() { | ||
23 | fn check(macro_body: &str, err: ParseError) { | ||
24 | let m = parse_macro_arm(macro_body); | ||
25 | assert_eq!(m, Err(err)); | ||
26 | } | ||
27 | check("invalid", ParseError::Expected("expected subtree".into())); | ||
28 | |||
29 | check("$i:ident => ()", ParseError::Expected("expected subtree".into())); | ||
30 | check("($i:ident) ()", ParseError::Expected("expected `=`".into())); | ||
31 | check("($($i:ident)_) => ()", ParseError::InvalidRepeat); | ||
32 | |||
33 | check("($i) => ($i)", ParseError::UnexpectedToken("bad fragment specifier 1".into())); | ||
34 | check("($i:) => ($i)", ParseError::UnexpectedToken("bad fragment specifier 1".into())); | ||
35 | } | ||
36 | |||
37 | fn parse_macro_arm(arm_definition: &str) -> Result<crate::MacroRules, ParseError> { | ||
38 | let macro_definition = format!(" macro_rules! m {{ {} }} ", arm_definition); | ||
39 | let source_file = ast::SourceFile::parse(¯o_definition).ok().unwrap(); | ||
40 | let macro_definition = | ||
41 | source_file.syntax().descendants().find_map(ast::MacroRules::cast).unwrap(); | ||
42 | |||
43 | let (definition_tt, _) = ast_to_token_tree(¯o_definition.token_tree().unwrap()).unwrap(); | ||
44 | crate::MacroRules::parse(&definition_tt) | ||
45 | } | ||
diff --git a/crates/parser/src/grammar/params.rs b/crates/parser/src/grammar/params.rs index e313f6fb7..9e2f02d43 100644 --- a/crates/parser/src/grammar/params.rs +++ b/crates/parser/src/grammar/params.rs | |||
@@ -41,22 +41,32 @@ fn list_(p: &mut Parser, flavor: Flavor) { | |||
41 | FnDef | FnTrait | FnPointer => (T!['('], T![')']), | 41 | FnDef | FnTrait | FnPointer => (T!['('], T![')']), |
42 | }; | 42 | }; |
43 | 43 | ||
44 | let m = p.start(); | 44 | let list_marker = p.start(); |
45 | p.bump(bra); | 45 | p.bump(bra); |
46 | 46 | ||
47 | let mut param_marker = None; | ||
47 | if let FnDef = flavor { | 48 | if let FnDef = flavor { |
48 | // test self_param_outer_attr | 49 | // test self_param_outer_attr |
49 | // fn f(#[must_use] self) {} | 50 | // fn f(#[must_use] self) {} |
50 | let m = p.start(); | 51 | let m = p.start(); |
51 | attributes::outer_attrs(p); | 52 | attributes::outer_attrs(p); |
52 | opt_self_param(p, m); | 53 | match opt_self_param(p, m) { |
54 | Ok(()) => {} | ||
55 | Err(m) => param_marker = Some(m), | ||
56 | } | ||
53 | } | 57 | } |
54 | 58 | ||
55 | while !p.at(EOF) && !p.at(ket) { | 59 | while !p.at(EOF) && !p.at(ket) { |
56 | // test param_outer_arg | 60 | // test param_outer_arg |
57 | // fn f(#[attr1] pat: Type) {} | 61 | // fn f(#[attr1] pat: Type) {} |
58 | let m = p.start(); | 62 | let m = match param_marker.take() { |
59 | attributes::outer_attrs(p); | 63 | Some(m) => m, |
64 | None => { | ||
65 | let m = p.start(); | ||
66 | attributes::outer_attrs(p); | ||
67 | m | ||
68 | } | ||
69 | }; | ||
60 | 70 | ||
61 | if !p.at_ts(PARAM_FIRST) { | 71 | if !p.at_ts(PARAM_FIRST) { |
62 | p.error("expected value parameter"); | 72 | p.error("expected value parameter"); |
@@ -72,8 +82,12 @@ fn list_(p: &mut Parser, flavor: Flavor) { | |||
72 | } | 82 | } |
73 | } | 83 | } |
74 | 84 | ||
85 | if let Some(m) = param_marker { | ||
86 | m.abandon(p); | ||
87 | } | ||
88 | |||
75 | p.expect(ket); | 89 | p.expect(ket); |
76 | m.complete(p, PARAM_LIST); | 90 | list_marker.complete(p, PARAM_LIST); |
77 | } | 91 | } |
78 | 92 | ||
79 | const PARAM_FIRST: TokenSet = patterns::PATTERN_FIRST.union(types::TYPE_FIRST); | 93 | const PARAM_FIRST: TokenSet = patterns::PATTERN_FIRST.union(types::TYPE_FIRST); |
@@ -153,7 +167,7 @@ fn variadic_param(p: &mut Parser) -> bool { | |||
153 | // fn d(&'a mut self, x: i32) {} | 167 | // fn d(&'a mut self, x: i32) {} |
154 | // fn e(mut self) {} | 168 | // fn e(mut self) {} |
155 | // } | 169 | // } |
156 | fn opt_self_param(p: &mut Parser, m: Marker) { | 170 | fn opt_self_param(p: &mut Parser, m: Marker) -> Result<(), Marker> { |
157 | if p.at(T![self]) || p.at(T![mut]) && p.nth(1) == T![self] { | 171 | if p.at(T![self]) || p.at(T![mut]) && p.nth(1) == T![self] { |
158 | p.eat(T![mut]); | 172 | p.eat(T![mut]); |
159 | self_as_name(p); | 173 | self_as_name(p); |
@@ -176,7 +190,7 @@ fn opt_self_param(p: &mut Parser, m: Marker) { | |||
176 | | (T![&], LIFETIME_IDENT, T![self], _) | 190 | | (T![&], LIFETIME_IDENT, T![self], _) |
177 | | (T![&], LIFETIME_IDENT, T![mut], T![self]) | 191 | | (T![&], LIFETIME_IDENT, T![mut], T![self]) |
178 | ) { | 192 | ) { |
179 | return m.abandon(p); | 193 | return Err(m); |
180 | } | 194 | } |
181 | p.bump(T![&]); | 195 | p.bump(T![&]); |
182 | if p.at(LIFETIME_IDENT) { | 196 | if p.at(LIFETIME_IDENT) { |
@@ -189,6 +203,7 @@ fn opt_self_param(p: &mut Parser, m: Marker) { | |||
189 | if !p.at(T![')']) { | 203 | if !p.at(T![')']) { |
190 | p.expect(T![,]); | 204 | p.expect(T![,]); |
191 | } | 205 | } |
206 | Ok(()) | ||
192 | } | 207 | } |
193 | 208 | ||
194 | fn self_as_name(p: &mut Parser) { | 209 | fn self_as_name(p: &mut Parser) { |
diff --git a/crates/rust-analyzer/src/semantic_tokens.rs b/crates/rust-analyzer/src/semantic_tokens.rs index be0bea00b..0cb7d12a7 100644 --- a/crates/rust-analyzer/src/semantic_tokens.rs +++ b/crates/rust-analyzer/src/semantic_tokens.rs | |||
@@ -45,15 +45,16 @@ define_semantic_token_types![ | |||
45 | (BRACKET, "bracket"), | 45 | (BRACKET, "bracket"), |
46 | (BUILTIN_TYPE, "builtinType"), | 46 | (BUILTIN_TYPE, "builtinType"), |
47 | (CHAR_LITERAL, "characterLiteral"), | 47 | (CHAR_LITERAL, "characterLiteral"), |
48 | (COMMA, "comma"), | ||
49 | (COLON, "colon"), | 48 | (COLON, "colon"), |
49 | (COMMA, "comma"), | ||
50 | (CONST_PARAMETER, "constParameter"), | ||
50 | (DOT, "dot"), | 51 | (DOT, "dot"), |
51 | (ESCAPE_SEQUENCE, "escapeSequence"), | 52 | (ESCAPE_SEQUENCE, "escapeSequence"), |
52 | (FORMAT_SPECIFIER, "formatSpecifier"), | 53 | (FORMAT_SPECIFIER, "formatSpecifier"), |
53 | (GENERIC, "generic"), | 54 | (GENERIC, "generic"), |
54 | (CONST_PARAMETER, "constParameter"), | 55 | (INTRA_DOC_LINK, "intraDocLink"), |
55 | (LIFETIME, "lifetime"), | ||
56 | (LABEL, "label"), | 56 | (LABEL, "label"), |
57 | (LIFETIME, "lifetime"), | ||
57 | (PARENTHESIS, "parenthesis"), | 58 | (PARENTHESIS, "parenthesis"), |
58 | (PUNCTUATION, "punctuation"), | 59 | (PUNCTUATION, "punctuation"), |
59 | (SELF_KEYWORD, "selfKeyword"), | 60 | (SELF_KEYWORD, "selfKeyword"), |
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index c63fe2915..70501618e 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs | |||
@@ -435,19 +435,20 @@ fn semantic_token_type_and_modifiers( | |||
435 | SymbolKind::Trait => lsp_types::SemanticTokenType::INTERFACE, | 435 | SymbolKind::Trait => lsp_types::SemanticTokenType::INTERFACE, |
436 | SymbolKind::Macro => lsp_types::SemanticTokenType::MACRO, | 436 | SymbolKind::Macro => lsp_types::SemanticTokenType::MACRO, |
437 | }, | 437 | }, |
438 | HlTag::Attribute => semantic_tokens::ATTRIBUTE, | ||
439 | HlTag::BoolLiteral => semantic_tokens::BOOLEAN, | ||
438 | HlTag::BuiltinType => semantic_tokens::BUILTIN_TYPE, | 440 | HlTag::BuiltinType => semantic_tokens::BUILTIN_TYPE, |
439 | HlTag::None => semantic_tokens::GENERIC, | ||
440 | HlTag::ByteLiteral | HlTag::NumericLiteral => lsp_types::SemanticTokenType::NUMBER, | 441 | HlTag::ByteLiteral | HlTag::NumericLiteral => lsp_types::SemanticTokenType::NUMBER, |
441 | HlTag::BoolLiteral => semantic_tokens::BOOLEAN, | ||
442 | HlTag::StringLiteral => lsp_types::SemanticTokenType::STRING, | ||
443 | HlTag::CharLiteral => semantic_tokens::CHAR_LITERAL, | 442 | HlTag::CharLiteral => semantic_tokens::CHAR_LITERAL, |
444 | HlTag::Comment => lsp_types::SemanticTokenType::COMMENT, | 443 | HlTag::Comment => lsp_types::SemanticTokenType::COMMENT, |
445 | HlTag::Attribute => semantic_tokens::ATTRIBUTE, | 444 | HlTag::EscapeSequence => semantic_tokens::ESCAPE_SEQUENCE, |
446 | HlTag::Keyword => lsp_types::SemanticTokenType::KEYWORD, | ||
447 | HlTag::UnresolvedReference => semantic_tokens::UNRESOLVED_REFERENCE, | ||
448 | HlTag::FormatSpecifier => semantic_tokens::FORMAT_SPECIFIER, | 445 | HlTag::FormatSpecifier => semantic_tokens::FORMAT_SPECIFIER, |
446 | HlTag::IntraDocLink => semantic_tokens::INTRA_DOC_LINK, | ||
447 | HlTag::Keyword => lsp_types::SemanticTokenType::KEYWORD, | ||
448 | HlTag::None => semantic_tokens::GENERIC, | ||
449 | HlTag::Operator => lsp_types::SemanticTokenType::OPERATOR, | 449 | HlTag::Operator => lsp_types::SemanticTokenType::OPERATOR, |
450 | HlTag::EscapeSequence => semantic_tokens::ESCAPE_SEQUENCE, | 450 | HlTag::StringLiteral => lsp_types::SemanticTokenType::STRING, |
451 | HlTag::UnresolvedReference => semantic_tokens::UNRESOLVED_REFERENCE, | ||
451 | HlTag::Punctuation(punct) => match punct { | 452 | HlTag::Punctuation(punct) => match punct { |
452 | HlPunct::Bracket => semantic_tokens::BRACKET, | 453 | HlPunct::Bracket => semantic_tokens::BRACKET, |
453 | HlPunct::Brace => semantic_tokens::BRACE, | 454 | HlPunct::Brace => semantic_tokens::BRACE, |
diff --git a/crates/syntax/src/ast.rs b/crates/syntax/src/ast.rs index 19261686c..38e0b04ef 100644 --- a/crates/syntax/src/ast.rs +++ b/crates/syntax/src/ast.rs | |||
@@ -118,7 +118,7 @@ fn test_doc_comment_none() { | |||
118 | .ok() | 118 | .ok() |
119 | .unwrap(); | 119 | .unwrap(); |
120 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); | 120 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); |
121 | assert!(module.doc_comment_text().is_none()); | 121 | assert!(module.doc_comments().doc_comment_text().is_none()); |
122 | } | 122 | } |
123 | 123 | ||
124 | #[test] | 124 | #[test] |
@@ -133,7 +133,7 @@ fn test_outer_doc_comment_of_items() { | |||
133 | .ok() | 133 | .ok() |
134 | .unwrap(); | 134 | .unwrap(); |
135 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); | 135 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); |
136 | assert_eq!("doc", module.doc_comment_text().unwrap()); | 136 | assert_eq!(" doc", module.doc_comments().doc_comment_text().unwrap()); |
137 | } | 137 | } |
138 | 138 | ||
139 | #[test] | 139 | #[test] |
@@ -148,7 +148,7 @@ fn test_inner_doc_comment_of_items() { | |||
148 | .ok() | 148 | .ok() |
149 | .unwrap(); | 149 | .unwrap(); |
150 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); | 150 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); |
151 | assert!(module.doc_comment_text().is_none()); | 151 | assert!(module.doc_comments().doc_comment_text().is_none()); |
152 | } | 152 | } |
153 | 153 | ||
154 | #[test] | 154 | #[test] |
@@ -162,7 +162,7 @@ fn test_doc_comment_of_statics() { | |||
162 | .ok() | 162 | .ok() |
163 | .unwrap(); | 163 | .unwrap(); |
164 | let st = file.syntax().descendants().find_map(Static::cast).unwrap(); | 164 | let st = file.syntax().descendants().find_map(Static::cast).unwrap(); |
165 | assert_eq!("Number of levels", st.doc_comment_text().unwrap()); | 165 | assert_eq!(" Number of levels", st.doc_comments().doc_comment_text().unwrap()); |
166 | } | 166 | } |
167 | 167 | ||
168 | #[test] | 168 | #[test] |
@@ -181,7 +181,10 @@ fn test_doc_comment_preserves_indents() { | |||
181 | .ok() | 181 | .ok() |
182 | .unwrap(); | 182 | .unwrap(); |
183 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); | 183 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); |
184 | assert_eq!("doc1\n```\nfn foo() {\n // ...\n}\n```", module.doc_comment_text().unwrap()); | 184 | assert_eq!( |
185 | " doc1\n ```\n fn foo() {\n // ...\n }\n ```", | ||
186 | module.doc_comments().doc_comment_text().unwrap() | ||
187 | ); | ||
185 | } | 188 | } |
186 | 189 | ||
187 | #[test] | 190 | #[test] |
@@ -198,7 +201,7 @@ fn test_doc_comment_preserves_newlines() { | |||
198 | .ok() | 201 | .ok() |
199 | .unwrap(); | 202 | .unwrap(); |
200 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); | 203 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); |
201 | assert_eq!("this\nis\nmod\nfoo", module.doc_comment_text().unwrap()); | 204 | assert_eq!(" this\n is\n mod\n foo", module.doc_comments().doc_comment_text().unwrap()); |
202 | } | 205 | } |
203 | 206 | ||
204 | #[test] | 207 | #[test] |
@@ -212,7 +215,7 @@ fn test_doc_comment_single_line_block_strips_suffix() { | |||
212 | .ok() | 215 | .ok() |
213 | .unwrap(); | 216 | .unwrap(); |
214 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); | 217 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); |
215 | assert_eq!("this is mod foo", module.doc_comment_text().unwrap()); | 218 | assert_eq!(" this is mod foo", module.doc_comments().doc_comment_text().unwrap()); |
216 | } | 219 | } |
217 | 220 | ||
218 | #[test] | 221 | #[test] |
@@ -226,7 +229,7 @@ fn test_doc_comment_single_line_block_strips_suffix_whitespace() { | |||
226 | .ok() | 229 | .ok() |
227 | .unwrap(); | 230 | .unwrap(); |
228 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); | 231 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); |
229 | assert_eq!("this is mod foo ", module.doc_comment_text().unwrap()); | 232 | assert_eq!(" this is mod foo ", module.doc_comments().doc_comment_text().unwrap()); |
230 | } | 233 | } |
231 | 234 | ||
232 | #[test] | 235 | #[test] |
@@ -245,8 +248,8 @@ fn test_doc_comment_multi_line_block_strips_suffix() { | |||
245 | .unwrap(); | 248 | .unwrap(); |
246 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); | 249 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); |
247 | assert_eq!( | 250 | assert_eq!( |
248 | " this\n is\n mod foo\n ", | 251 | "\n this\n is\n mod foo\n ", |
249 | module.doc_comment_text().unwrap() | 252 | module.doc_comments().doc_comment_text().unwrap() |
250 | ); | 253 | ); |
251 | } | 254 | } |
252 | 255 | ||
@@ -259,8 +262,8 @@ fn test_comments_preserve_trailing_whitespace() { | |||
259 | .unwrap(); | 262 | .unwrap(); |
260 | let def = file.syntax().descendants().find_map(Struct::cast).unwrap(); | 263 | let def = file.syntax().descendants().find_map(Struct::cast).unwrap(); |
261 | assert_eq!( | 264 | assert_eq!( |
262 | "Representation of a Realm. \nIn the specification these are called Realm Records.", | 265 | " Representation of a Realm. \n In the specification these are called Realm Records.", |
263 | def.doc_comment_text().unwrap() | 266 | def.doc_comments().doc_comment_text().unwrap() |
264 | ); | 267 | ); |
265 | } | 268 | } |
266 | 269 | ||
@@ -276,7 +279,7 @@ fn test_four_slash_line_comment() { | |||
276 | .ok() | 279 | .ok() |
277 | .unwrap(); | 280 | .unwrap(); |
278 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); | 281 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); |
279 | assert_eq!("doc comment", module.doc_comment_text().unwrap()); | 282 | assert_eq!(" doc comment", module.doc_comments().doc_comment_text().unwrap()); |
280 | } | 283 | } |
281 | 284 | ||
282 | #[test] | 285 | #[test] |
diff --git a/crates/syntax/src/ast/token_ext.rs b/crates/syntax/src/ast/token_ext.rs index 977eb8181..6c242d126 100644 --- a/crates/syntax/src/ast/token_ext.rs +++ b/crates/syntax/src/ast/token_ext.rs | |||
@@ -33,23 +33,20 @@ impl ast::Comment { | |||
33 | prefix | 33 | prefix |
34 | } | 34 | } |
35 | 35 | ||
36 | /// Returns the textual content of a doc comment block as a single string. | 36 | /// Returns the textual content of a doc comment node as a single string with prefix and suffix |
37 | /// That is, strips leading `///` (+ optional 1 character of whitespace), | 37 | /// removed. |
38 | /// trailing `*/`, trailing whitespace and then joins the lines. | ||
39 | pub fn doc_comment(&self) -> Option<&str> { | 38 | pub fn doc_comment(&self) -> Option<&str> { |
40 | let kind = self.kind(); | 39 | let kind = self.kind(); |
41 | match kind { | 40 | match kind { |
42 | CommentKind { shape, doc: Some(_) } => { | 41 | CommentKind { shape, doc: Some(_) } => { |
43 | let prefix = kind.prefix(); | 42 | let prefix = kind.prefix(); |
44 | let text = &self.text()[prefix.len()..]; | 43 | let text = &self.text()[prefix.len()..]; |
45 | let ws = text.chars().next().filter(|c| c.is_whitespace()); | 44 | let text = if shape == CommentShape::Block { |
46 | let text = ws.map_or(text, |ws| &text[ws.len_utf8()..]); | 45 | text.strip_suffix("*/").unwrap_or(text) |
47 | match shape { | 46 | } else { |
48 | CommentShape::Block if text.ends_with("*/") => { | 47 | text |
49 | Some(&text[..text.len() - "*/".len()]) | 48 | }; |
50 | } | 49 | Some(text) |
51 | _ => Some(text), | ||
52 | } | ||
53 | } | 50 | } |
54 | _ => None, | 51 | _ => None, |
55 | } | 52 | } |
diff --git a/crates/syntax/src/ast/traits.rs b/crates/syntax/src/ast/traits.rs index 13a769d51..ddd213637 100644 --- a/crates/syntax/src/ast/traits.rs +++ b/crates/syntax/src/ast/traits.rs | |||
@@ -1,8 +1,6 @@ | |||
1 | //! Various traits that are implemented by ast nodes. | 1 | //! Various traits that are implemented by ast nodes. |
2 | //! | 2 | //! |
3 | //! The implementations are usually trivial, and live in generated.rs | 3 | //! The implementations are usually trivial, and live in generated.rs |
4 | use itertools::Itertools; | ||
5 | |||
6 | use crate::{ | 4 | use crate::{ |
7 | ast::{self, support, AstChildren, AstNode, AstToken}, | 5 | ast::{self, support, AstChildren, AstNode, AstToken}, |
8 | syntax_node::SyntaxElementChildren, | 6 | syntax_node::SyntaxElementChildren, |
@@ -72,14 +70,10 @@ pub trait AttrsOwner: AstNode { | |||
72 | } | 70 | } |
73 | } | 71 | } |
74 | 72 | ||
75 | pub trait DocCommentsOwner: AstNode { | 73 | pub trait DocCommentsOwner: AttrsOwner { |
76 | fn doc_comments(&self) -> CommentIter { | 74 | fn doc_comments(&self) -> CommentIter { |
77 | CommentIter { iter: self.syntax().children_with_tokens() } | 75 | CommentIter { iter: self.syntax().children_with_tokens() } |
78 | } | 76 | } |
79 | |||
80 | fn doc_comment_text(&self) -> Option<String> { | ||
81 | self.doc_comments().doc_comment_text() | ||
82 | } | ||
83 | } | 77 | } |
84 | 78 | ||
85 | impl CommentIter { | 79 | impl CommentIter { |
@@ -87,12 +81,12 @@ impl CommentIter { | |||
87 | CommentIter { iter: syntax_node.children_with_tokens() } | 81 | CommentIter { iter: syntax_node.children_with_tokens() } |
88 | } | 82 | } |
89 | 83 | ||
90 | /// Returns the textual content of a doc comment block as a single string. | 84 | #[cfg(test)] |
91 | /// That is, strips leading `///` (+ optional 1 character of whitespace), | ||
92 | /// trailing `*/`, trailing whitespace and then joins the lines. | ||
93 | pub fn doc_comment_text(self) -> Option<String> { | 85 | pub fn doc_comment_text(self) -> Option<String> { |
94 | let docs = | 86 | let docs = itertools::Itertools::join( |
95 | self.filter_map(|comment| comment.doc_comment().map(ToOwned::to_owned)).join("\n"); | 87 | &mut self.filter_map(|comment| comment.doc_comment().map(ToOwned::to_owned)), |
88 | "\n", | ||
89 | ); | ||
96 | if docs.is_empty() { | 90 | if docs.is_empty() { |
97 | None | 91 | None |
98 | } else { | 92 | } else { |
diff --git a/crates/syntax/test_data/parser/inline/ok/0139_param_outer_arg.rast b/crates/syntax/test_data/parser/inline/ok/0139_param_outer_arg.rast index 495e4c51b..a84088bf3 100644 --- a/crates/syntax/test_data/parser/inline/ok/0139_param_outer_arg.rast +++ b/crates/syntax/test_data/parser/inline/ok/0139_param_outer_arg.rast | |||
@@ -6,16 +6,16 @@ [email protected] | |||
6 | [email protected] "f" | 6 | [email protected] "f" |
7 | [email protected] | 7 | [email protected] |
8 | [email protected] "(" | 8 | [email protected] "(" |
9 | ATTR@5..13 | 9 | PARAM@5..23 |
10 | POUND@5..6 "#" | 10 | ATTR@5..13 |
11 | L_BRACK@6..7 "[" | 11 | POUND@5..6 "#" |
12 | PATH@7..12 | 12 | L_BRACK@6..7 "[" |
13 | PATH_SEGMENT@7..12 | 13 | [email protected] |
14 | NAME_REF@7..12 | 14 | PATH_SEGMENT@7..12 |
15 | IDENT@7..12 "attr1" | 15 | NAME_REF@7..12 |
16 | R_BRACK@12..13 "]" | 16 | IDENT@7..12 "attr1" |
17 | WHITESPACE@13..14 " " | 17 | R_BRACK@12..13 "]" |
18 | PARAM@14..23 | 18 | WHITESPACE@13..14 " " |
19 | [email protected] | 19 | [email protected] |
20 | [email protected] | 20 | [email protected] |
21 | [email protected] "pat" | 21 | [email protected] "pat" |
diff --git a/crates/syntax/test_data/parser/ok/0051_parameter_attrs.rast b/crates/syntax/test_data/parser/ok/0051_parameter_attrs.rast index e10521d85..88470c41c 100644 --- a/crates/syntax/test_data/parser/ok/0051_parameter_attrs.rast +++ b/crates/syntax/test_data/parser/ok/0051_parameter_attrs.rast | |||
@@ -6,25 +6,25 @@ [email protected] | |||
6 | [email protected] "g1" | 6 | [email protected] "g1" |
7 | [email protected] | 7 | [email protected] |
8 | [email protected] "(" | 8 | [email protected] "(" |
9 | ATTR@6..14 | 9 | PARAM@6..33 |
10 | POUND@6..7 "#" | 10 | ATTR@6..14 |
11 | L_BRACK@7..8 "[" | 11 | POUND@6..7 "#" |
12 | PATH@8..13 | 12 | L_BRACK@7..8 "[" |
13 | PATH_SEGMENT@8..13 | 13 | [email protected] |
14 | NAME_REF@8..13 | 14 | PATH_SEGMENT@8..13 |
15 | IDENT@8..13 "attr1" | 15 | NAME_REF@8..13 |
16 | R_BRACK@13..14 "]" | 16 | IDENT@8..13 "attr1" |
17 | WHITESPACE@14..15 " " | 17 | R_BRACK@13..14 "]" |
18 | ATTR@15..23 | 18 | WHITESPACE@14..15 " " |
19 | POUND@15..16 "#" | 19 | ATTR@15..23 |
20 | L_BRACK@16..17 "[" | 20 | POUND@15..16 "#" |
21 | PATH@17..22 | 21 | L_BRACK@16..17 "[" |
22 | PATH_SEGMENT@17..22 | 22 | [email protected] |
23 | NAME_REF@17..22 | 23 | PATH_SEGMENT@17..22 |
24 | IDENT@17..22 "attr2" | 24 | NAME_REF@17..22 |
25 | R_BRACK@22..23 "]" | 25 | IDENT@17..22 "attr2" |
26 | WHITESPACE@23..24 " " | 26 | R_BRACK@22..23 "]" |
27 | PARAM@24..33 | 27 | WHITESPACE@23..24 " " |
28 | [email protected] | 28 | [email protected] |
29 | [email protected] | 29 | [email protected] |
30 | [email protected] "pat" | 30 | [email protected] "pat" |
@@ -48,16 +48,16 @@ [email protected] | |||
48 | [email protected] "g2" | 48 | [email protected] "g2" |
49 | [email protected] | 49 | [email protected] |
50 | [email protected] "(" | 50 | [email protected] "(" |
51 | ATT[email protected]2 | 51 | PARAM@44..58 |
52 | POUND@44..45 "#" | 52 | ATTR@44..52 |
53 | L_BRACK@45..46 "[" | 53 | POUND@44..45 "#" |
54 | PATH@46..51 | 54 | L_BRACK@45..46 "[" |
55 | PATH_SEGMENT@46..51 | 55 | [email protected] |
56 | NAME_REF@46..51 | 56 | PATH_SEGMENT@46..51 |
57 | IDENT@46..51 "attr1" | 57 | NAME_REF@46..51 |
58 | R_BRACK@51..52 "]" | 58 | IDENT@46..51 "attr1" |
59 | WHITESPACE@52..53 " " | 59 | R_BRACK@51..52 "]" |
60 | PARAM@53..58 | 60 | WHITESPACE@52..53 " " |
61 | [email protected] | 61 | [email protected] |
62 | [email protected] | 62 | [email protected] |
63 | [email protected] "x" | 63 | [email protected] "x" |
@@ -203,16 +203,16 @@ [email protected] | |||
203 | [email protected] "bar" | 203 | [email protected] "bar" |
204 | [email protected] | 204 | [email protected] |
205 | [email protected] "(" | 205 | [email protected] "(" |
206 | ATT[email protected]04 | 206 | PARAM@197..211 |
207 | POUND@197..198 "#" | 207 | ATTR@197..204 |
208 | L_BRACK@198..199 "[" | 208 | POUND@197..198 "#" |
209 | PATH@199..203 | 209 | L_BRACK@198..199 "[" |
210 | PATH_SEGMENT@199..203 | 210 | [email protected] |
211 | NAME_REF@199..203 | 211 | PATH_SEGMENT@199..203 |
212 | IDENT@199..203 "attr" | 212 | NAME_REF@199..203 |
213 | R_BRACK@203..204 "]" | 213 | IDENT@199..203 "attr" |
214 | WHITESPACE@204..205 " " | 214 | R_BRACK@203..204 "]" |
215 | PARAM@205..211 | 215 | WHITESPACE@204..205 " " |
216 | [email protected] | 216 | [email protected] |
217 | [email protected] "_" | 217 | [email protected] "_" |
218 | [email protected] ":" | 218 | [email protected] ":" |