aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/hir/src/attrs.rs5
-rw-r--r--crates/hir/src/db.rs8
-rw-r--r--crates/hir/src/lib.rs2
-rw-r--r--crates/hir/src/semantics.rs3
-rw-r--r--crates/hir/src/semantics/source_to_def.rs6
-rw-r--r--crates/hir_def/src/adt.rs6
-rw-r--r--crates/hir_def/src/attr.rs146
-rw-r--r--crates/hir_def/src/body/lower.rs27
-rw-r--r--crates/hir_def/src/body/tests.rs2
-rw-r--r--crates/hir_def/src/data.rs62
-rw-r--r--crates/hir_def/src/db.rs4
-rw-r--r--crates/hir_def/src/generics.rs14
-rw-r--r--crates/hir_def/src/item_tree.rs68
-rw-r--r--crates/hir_def/src/item_tree/lower.rs41
-rw-r--r--crates/hir_def/src/nameres.rs2
-rw-r--r--crates/hir_def/src/nameres/collector.rs58
-rw-r--r--crates/hir_def/src/nameres/tests.rs19
-rw-r--r--crates/hir_def/src/nameres/tests/diagnostics.rs19
-rw-r--r--crates/hir_def/src/src.rs16
-rw-r--r--crates/hir_expand/src/builtin_macro.rs21
-rw-r--r--crates/hir_ty/src/diagnostics/expr.rs57
-rw-r--r--crates/hir_ty/src/tests/simple.rs19
-rw-r--r--crates/hir_ty/src/tests/traits.rs21
-rw-r--r--crates/ide/src/doc_links.rs13
-rw-r--r--crates/ide/src/goto_definition.rs24
-rw-r--r--crates/ide/src/hover.rs60
-rw-r--r--crates/ide/src/runnables.rs48
-rw-r--r--crates/ide/src/syntax_highlighting.rs2
-rw-r--r--crates/ide/src/syntax_highlighting/html.rs1
-rw-r--r--crates/ide/src/syntax_highlighting/inject.rs261
-rw-r--r--crates/ide/src/syntax_highlighting/tags.rs10
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html1
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html42
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html1
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_injection.html1
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_strings.html1
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html1
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlighting.html1
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/injection.html1
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html1
-rw-r--r--crates/ide/src/syntax_highlighting/tests.rs41
-rw-r--r--crates/ide_completion/src/lib.rs2
-rw-r--r--crates/ide_db/src/apply_change.rs2
-rw-r--r--crates/ide_db/src/call_info.rs6
-rw-r--r--crates/ide_db/src/call_info/tests.rs82
-rw-r--r--crates/mbe/src/tests.rs1994
-rw-r--r--crates/mbe/src/tests/expand.rs1872
-rw-r--r--crates/mbe/src/tests/rule.rs45
-rw-r--r--crates/parser/src/grammar/params.rs29
-rw-r--r--crates/rust-analyzer/src/semantic_tokens.rs7
-rw-r--r--crates/rust-analyzer/src/to_proto.rs15
-rw-r--r--crates/syntax/src/ast.rs29
-rw-r--r--crates/syntax/src/ast/token_ext.rs19
-rw-r--r--crates/syntax/src/ast/traits.rs18
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0139_param_outer_arg.rast20
-rw-r--r--crates/syntax/test_data/parser/ok/0051_parameter_attrs.rast78
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;
11use syntax::ast; 11use syntax::ast;
12 12
13use crate::{ 13use 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
18pub trait HasAttrs { 18pub 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
69macro_rules! impl_has_attrs_enum { 70macro_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 @@
3pub use hir_def::db::{ 3pub 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};
12pub use hir_expand::db::{ 12pub 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::{
89pub use { 89pub 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
753to_def_impls![ 753to_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 {
76impl RawAttrs { 76impl 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
381pub struct AttrSourceMap {
382 attrs: Vec<Either<ast::Attr, ast::Comment>>,
383}
384
385impl 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)]
369pub struct Attr { 400pub struct Attr {
370 index: u32, 401 index: u32,
@@ -381,7 +412,7 @@ pub enum AttrInput {
381} 412}
382 413
383impl Attr { 414impl 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
494fn attrs_from_item_tree<N: ItemTreeNode>(id: ItemTreeId<N>, db: &dyn DefDatabase) -> RawAttrs { 532fn 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
500fn collect_attrs(owner: &dyn AttrsOwner) -> impl Iterator<Item = Either<ast::Attr, ast::Comment>> { 538fn 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(|&param| 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 {
96impl TraitData { 115impl 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 {
183impl ConstData { 202impl 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 {
206impl StaticData { 225impl 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)]
50pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> { 50pub 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
78impl ItemTree { 78impl 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
343macro_rules! from_attrs { 347macro_rules! from_attrs {
@@ -352,7 +356,7 @@ macro_rules! from_attrs {
352 }; 356 };
353} 357}
354 358
355from_attrs!(ModItem(ModItem), Variant(Idx<Variant>), Field(Idx<Field>)); 359from_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.
358pub trait ItemTreeNode: Clone { 362pub trait ItemTreeNode: Clone {
@@ -401,7 +405,47 @@ impl<N: ItemTreeNode> fmt::Debug for FileItemTreeId<N> {
401 } 405 }
402} 406}
403 407
404pub type ItemTreeId<N> = InFile<FileItemTreeId<N>>; 408#[derive(Debug)]
409pub struct ItemTreeId<N: ItemTreeNode> {
410 file: HirFileId,
411 pub value: FileItemTreeId<N>,
412}
413
414impl<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
428impl<N: ItemTreeNode> Copy for ItemTreeId<N> {}
429impl<N: ItemTreeNode> Clone for ItemTreeId<N> {
430 fn clone(&self) -> Self {
431 *self
432 }
433}
434
435impl<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
441impl<N: ItemTreeNode> Eq for ItemTreeId<N> {}
442
443impl<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
406macro_rules! mod_items { 450macro_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
491impl_index!(fields: Field, variants: Variant); 535impl_index!(fields: Field, variants: Variant, params: Param);
492 536
493impl Index<RawVisibilityId> for ItemTree { 537impl 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)]
617pub enum Param {
618 Normal(Idx<TypeRef>),
619 Varargs,
620}
621
573#[derive(Debug, Clone, PartialEq, Eq)] 622#[derive(Debug, Clone, PartialEq, Eq)]
574pub struct FunctionQualifier { 623pub 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.
803pub struct IdRange<T> { 853pub 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
871impl<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
821impl<T> fmt::Debug for IdRange<T> { 877impl<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(&param, &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
782fn desugar_future_path(orig: TypeRef) -> Path { 791fn 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> {
242impl DefCollector<'_> { 242impl 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]
718fn use_crate_as() {
719 check(
720 r#"
721use crate as foo;
722
723use foo::bar as baz;
724
725fn 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]
205fn 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(
333fn relative_file( 333fn 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#"
698struct C(#[cfg(FALSE)] ());
699impl C {
700 fn new() -> Self {
701 Self(
702 #[cfg(FALSE)]
703 (),
704 )
705 }
706
707 fn method(&self) {}
708}
709
710fn 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#"
721fn foo(#[cfg(NEVER)] x: ()) {}
722
723struct S;
724
725impl 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
731extern "C" {
732 fn fixed(fixed: u8, #[cfg(NEVER)] ...);
733 fn varargs(#[cfg(not(NEVER))] ...);
734}
735
736fn 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]
2550fn cfgd_out_assoc_items() {
2551 check_types(
2552 r#"
2553struct S;
2554
2555impl S {
2556 #[cfg(FALSE)]
2557 const C: S = S;
2558}
2559
2560fn 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]
3258fn nested_inner_function_calling_self() {
3259 check_infer(
3260 r#"
3261struct S;
3262fn 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 @@
1use std::ops::Range;
2
1use either::Either; 3use either::Either;
2use hir::{HasAttrs, ModuleDef, Semantics}; 4use hir::{HasAttrs, ModuleDef, Semantics};
3use ide_db::{ 5use ide_db::{
@@ -5,7 +7,8 @@ use ide_db::{
5 RootDatabase, 7 RootDatabase,
6}; 8};
7use syntax::{ 9use 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
11use crate::{ 14use 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
108fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> { 112fn 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)
1140pub fn bar() { } 1144pub 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/// ```
1537struct Bar; 1546struct Bar;
1538 1547
1539fn foo() { let bar = Ba$0r; } 1548fn 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*/
3454fn 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/// ```
577fn should_have_runnable_2() {} 577fn should_have_runnable_2() {}
578 578
579/**
580```rust
581let z = 55;
582```
583*/
584fn should_have_no_runnable_3() {}
585
586/**
587 ```rust
588 let z = 55;
589 ```
590*/
591fn 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() {}
616struct StructWithRunnable(String); 630struct 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
3use hir::Semantics; 3use std::{mem, ops::Range};
4use ide_db::call_info::ActiveParameter;
5use syntax::{ast, AstToken, SyntaxNode, SyntaxToken, TextRange, TextSize};
6 4
7use crate::{Analysis, HlMod, HlRange, HlTag, RootDatabase}; 5use either::Either;
6use hir::{HasAttrs, Semantics};
7use ide_db::{call_info::ActiveParameter, defs::Definition};
8use syntax::{
9 ast::{self, AstNode, AttrsOwner, DocCommentsOwner},
10 match_ast, AstToken, NodeOrToken, SyntaxNode, SyntaxToken, TextRange, TextSize,
11};
12
13use crate::{
14 doc_links::extract_definitions_from_markdown, Analysis, HlMod, HlRange, HlTag, RootDatabase,
15};
8 16
9use super::{highlights::Highlights, injector::Injector}; 17use 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
85pub(super) fn doc_comment(hl: &mut Highlights, node: &SyntaxNode) { 93struct 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()); 97impl AttrsOwnerNode {
98 fn new<N: DocCommentsOwner>(node: N) -> Self {
99 AttrsOwnerNode { node: node.syntax().clone() }
100 }
101}
102
103impl AttrsOwner for AttrsOwnerNode {}
104impl 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)) { 122fn 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.
151pub(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
279fn 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
303fn 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);
18pub enum HlTag { 18pub 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">&gt;</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">&gt;</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">/**
133It 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]
471fn test_highlight_doctest() { 471fn 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
539pub 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/// ```
564pub fn mix_and_match() {}
565
566/**
567It is beyond me why you'd use these when you got ///
568```rust
569let _ = example(&[1, 2, 3]);
570```
571 */
572pub fn block_comments() {}
573
574/**
575 Really, I don't get it
576 ```rust
577 let _ = example(&[1, 2, 3]);
578 ```
579*/
580pub 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 @@
1mod expand;
2mod rule;
3
1use std::fmt::Write; 4use std::fmt::Write;
2 5
3use ::parser::FragmentKind; 6use ::parser::FragmentKind;
4use syntax::{ 7use syntax::{ast, AstNode, NodeOrToken, SyntaxNode, WalkEvent};
5 ast, AstNode, NodeOrToken,
6 SyntaxKind::{ERROR, IDENT},
7 SyntaxNode, WalkEvent, T,
8};
9use test_utils::assert_eq_text; 8use test_utils::assert_eq_text;
10 9
11use super::*; 10use super::*;
12 11
13mod 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(&macro_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(&macro_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]
70fn test_token_id_shift() {
71 let expansion = parse_macro(
72 r#"
73macro_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]
100fn test_token_map() {
101 let expanded = parse_macro(
102 r#"
103macro_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]
127fn test_convert_tt() {
128 parse_macro(r#"
129macro_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]
149fn test_convert_tt2() {
150 parse_macro(
151 r#"
152macro_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#"
168SUBTREE $
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]
222fn test_lifetime_split() {
223 parse_macro(
224 r#"
225macro_rules! foo {
226 ($($t:tt)*) => { $($t)*}
227}
228"#,
229 )
230 .assert_expand(
231 r#"foo!(static bar: &'static str = "hello";);"#,
232 r#"
233SUBTREE $
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]
249fn 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]
291fn 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]
313fn 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]
335fn 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]
357fn 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]
380fn 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]
394fn 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]
408fn 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]
422fn 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]
436fn 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]
449fn 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]
460fn 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]
471fn 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]
482fn 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]
493fn 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
544fn 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}
550fn 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
557fn 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]
565fn 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]
594fn 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]
610fn 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]
624fn 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]
674fn 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]
688fn test_parse_macro_def_simple() {
689 cov_mark::check!(parse_macro_def_simple);
690
691 parse_macro2(
692 r#"
693macro foo($id:ident) {
694 fn $id() {}
695}
696"#,
697 )
698 .assert_expand_items("foo!(bar);", "fn bar () {}");
699}
700
701#[test]
702fn test_parse_macro_def_rules() {
703 cov_mark::check!(parse_macro_def_rules);
704
705 parse_macro2(
706 r#"
707macro 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]
721fn 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]
739fn 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]
753fn 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]
767fn 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]
784fn 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]
807fn test_expr_with_attr() {
808 parse_macro(
809 r#"
810macro_rules! m {
811 ($a:expr) => {0}
812}
813"#,
814 )
815 .assert_expand_items("m!(#[allow(a)]())", "0");
816}
817
818#[test]
819fn 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]
833fn 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]
856fn 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]
868fn 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]
883fn 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]
897fn 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]
930fn 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]
942fn 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]
961fn 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]
984fn 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]
1007fn 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]
1019fn 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]
1031fn 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]
1043fn 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]
1074fn 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]
1090fn 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]
1102fn test_underscore_not_greedily() {
1103 parse_macro(
1104 r#"
1105macro_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#"
1115macro_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]
1125fn test_underscore_as_type() {
1126 parse_macro(
1127 r#"
1128macro_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]
1138fn 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]
1150fn test_dollar_crate_lhs_is_not_meta() {
1151 parse_macro(
1152 r#"
1153macro_rules! foo {
1154 ($crate) => {};
1155 () => {0};
1156}
1157 "#,
1158 )
1159 .assert_expand_items(r#"foo!{}"#, r#"0"#);
1160}
1161
1162#[test]
1163fn 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]
1175fn 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]
1196fn 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#"
1207SUBTREE $
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]
1226fn 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]
1240fn test_inner_macro_rules() {
1241 parse_macro(
1242 r#"
1243macro_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]
1265fn test_expr_after_path_colons() {
1266 assert!(parse_macro(
1267 r#"
1268macro_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]
1282fn test_match_is_not_greedy() {
1283 parse_macro(
1284 r#"
1285macro_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]
1295fn 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]
1388fn test_winapi_struct() {
1389 // from https://github.com/retep998/winapi-rs/blob/a7ef2bca086aae76cf6c4ce4c2552988ed9798ad/src/macros.rs#L366
1390
1391 parse_macro(
1392 r#"
1393macro_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]
1424fn test_int_base() {
1425 parse_macro(
1426 r#"
1427macro_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]
1444fn test_generate_pattern_iterators() {
1445 // from https://github.com/rust-lang/rust/blob/316a391dcb7d66dc25f1f9a4ec9d368ef7615005/src/libcore/str/mod.rs
1446 parse_macro(
1447 r#"
1448macro_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]
1464fn 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#"
1468macro_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#"
1508impl_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]
1529fn 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]
1546fn 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]
1564fn 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#"
1588cfg_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#"
1602cfg_if ! { @ __apply cfg ( all ( not ( any ( not ( any ( target_os = "solaris" , target_os = "illumos" ) ) ) ) ) ) , }
1603"#,
1604 "",
1605 );
1606}
1607
1608#[test]
1609fn test_proptest_arbitrary() {
1610 // from https://github.com/AltSysrq/proptest/blob/d1c4b049337d2f75dd6f49a095115f7c532e5129/proptest/src/arbitrary/macros.rs#L16
1611 parse_macro(
1612 r#"
1613macro_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]
1637fn 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]
1643macro_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]
1665fn test_quick_error() {
1666 let expanded = parse_macro(
1667 r#"
1668macro_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#"
1692quick_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]
1703fn test_empty_repeat_vars_in_empty_repeat_vars() {
1704 parse_macro(
1705 r#"
1706macro_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]
1758fn 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
1773pub(crate) struct MacroFixture { 12pub(crate) struct MacroFixture {
1774 rules: MacroRules, 13 rules: MacroRules,
1775} 14}
@@ -1889,6 +128,37 @@ macro_rules! impl_fixture {
1889impl_fixture!(MacroFixture); 128impl_fixture!(MacroFixture);
1890impl_fixture!(MacroFixture2); 129impl_fixture!(MacroFixture2);
1891 130
131pub(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
137pub(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
143pub(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
152pub(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
1892fn parse_macro_rules_to_tt(ra_fixture: &str) -> tt::Subtree { 162fn 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
1925pub(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
1931pub(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
1937pub(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
1946pub(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
1956fn debug_dump_ignore_spaces(node: &syntax::SyntaxNode) -> String { 195fn 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]
1993fn 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]
2022fn 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]
2039fn 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]
2058fn 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]
2113fn 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]
2133fn 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]
2143fn 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 @@
1use ::parser::FragmentKind;
2use syntax::{
3 SyntaxKind::{ERROR, IDENT},
4 T,
5};
6use test_utils::assert_eq_text;
7
8use 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]
18fn test_token_id_shift() {
19 let expansion = parse_macro(
20 r#"
21macro_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]
48fn test_token_map() {
49 let expanded = parse_macro(
50 r#"
51macro_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]
75fn test_convert_tt() {
76 parse_macro(r#"
77macro_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]
97fn test_convert_tt2() {
98 parse_macro(
99 r#"
100macro_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#"
116SUBTREE $
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]
170fn test_lifetime_split() {
171 parse_macro(
172 r#"
173macro_rules! foo {
174 ($($t:tt)*) => { $($t)*}
175}
176"#,
177 )
178 .assert_expand(
179 r#"foo!(static bar: &'static str = "hello";);"#,
180 r#"
181SUBTREE $
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]
197fn 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]
239fn 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]
261fn 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]
283fn 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]
305fn 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]
328fn 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]
342fn 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]
356fn 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]
370fn 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]
384fn 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]
397fn 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]
408fn 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]
419fn 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]
430fn 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]
441fn 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
492fn 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}
498fn 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
505fn 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]
513fn 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]
542fn 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]
558fn 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]
572fn 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]
622fn 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]
636fn test_parse_macro_def_simple() {
637 cov_mark::check!(parse_macro_def_simple);
638
639 parse_macro2(
640 r#"
641macro foo($id:ident) {
642 fn $id() {}
643}
644"#,
645 )
646 .assert_expand_items("foo!(bar);", "fn bar () {}");
647}
648
649#[test]
650fn test_parse_macro_def_rules() {
651 cov_mark::check!(parse_macro_def_rules);
652
653 parse_macro2(
654 r#"
655macro foo {
656 ($id:ident) => {
657 fn $id() {}
658 }
659}
660"#,
661 )
662 .assert_expand_items("foo!(bar);", "fn bar () {}");
663}
664
665#[test]
666fn 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]
684fn 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]
698fn 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]
712fn 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]
729fn 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]
752fn test_expr_with_attr() {
753 parse_macro(
754 r#"
755macro_rules! m {
756 ($a:expr) => {0}
757}
758"#,
759 )
760 .assert_expand_items("m!(#[allow(a)]())", "0");
761}
762
763#[test]
764fn 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]
778fn 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]
801fn 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]
813fn 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]
828fn 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]
842fn 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]
875fn 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]
887fn 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]
906fn 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]
929fn 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]
952fn 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]
964fn 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]
976fn 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]
988fn 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]
1019fn 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]
1035fn 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]
1047fn test_underscore_not_greedily() {
1048 parse_macro(
1049 r#"
1050macro_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#"
1060macro_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]
1070fn test_underscore_as_type() {
1071 parse_macro(
1072 r#"
1073macro_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]
1083fn 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]
1095fn test_dollar_crate_lhs_is_not_meta() {
1096 parse_macro(
1097 r#"
1098macro_rules! foo {
1099 ($crate) => {};
1100 () => {0};
1101}
1102 "#,
1103 )
1104 .assert_expand_items(r#"foo!{}"#, r#"0"#);
1105}
1106
1107#[test]
1108fn 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]
1120fn 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]
1141fn 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#"
1152SUBTREE $
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]
1171fn 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]
1185fn test_inner_macro_rules() {
1186 parse_macro(
1187 r#"
1188macro_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]
1210fn test_expr_after_path_colons() {
1211 assert!(parse_macro(
1212 r#"
1213macro_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]
1227fn test_match_is_not_greedy() {
1228 parse_macro(
1229 r#"
1230macro_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]
1240fn 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]
1333fn test_winapi_struct() {
1334 // from https://github.com/retep998/winapi-rs/blob/a7ef2bca086aae76cf6c4ce4c2552988ed9798ad/src/macros.rs#L366
1335
1336 parse_macro(
1337 r#"
1338macro_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]
1369fn test_int_base() {
1370 parse_macro(
1371 r#"
1372macro_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]
1389fn test_generate_pattern_iterators() {
1390 // from https://github.com/rust-lang/rust/blob/316a391dcb7d66dc25f1f9a4ec9d368ef7615005/src/libcore/str/mod.rs
1391 parse_macro(
1392 r#"
1393macro_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]
1409fn 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#"
1413macro_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#"
1453impl_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]
1474fn 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]
1491fn 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]
1509fn 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#"
1533cfg_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#"
1547cfg_if ! { @ __apply cfg ( all ( not ( any ( not ( any ( target_os = "solaris" , target_os = "illumos" ) ) ) ) ) ) , }
1548"#,
1549 "",
1550 );
1551}
1552
1553#[test]
1554fn test_proptest_arbitrary() {
1555 // from https://github.com/AltSysrq/proptest/blob/d1c4b049337d2f75dd6f49a095115f7c532e5129/proptest/src/arbitrary/macros.rs#L16
1556 parse_macro(
1557 r#"
1558macro_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]
1582fn 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]
1588macro_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]
1610fn test_quick_error() {
1611 let expanded = parse_macro(
1612 r#"
1613macro_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#"
1637quick_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]
1648fn test_empty_repeat_vars_in_empty_repeat_vars() {
1649 parse_macro(
1650 r#"
1651macro_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]
1696fn 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]
1712fn 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]
1741fn 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]
1758fn 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]
1777fn 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]
1832fn 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]
1852fn 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]
1862fn 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 @@
1use syntax::{ast, AstNode};
2
3use crate::ast_to_token_tree;
4
5use super::*;
6
7#[test]
8fn 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]
22fn 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
37fn 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(&macro_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(&macro_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
79const PARAM_FIRST: TokenSet = patterns::PATTERN_FIRST.union(types::TYPE_FIRST); 93const 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// }
156fn opt_self_param(p: &mut Parser, m: Marker) { 170fn 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
194fn self_as_name(p: &mut Parser) { 209fn 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
4use itertools::Itertools;
5
6use crate::{ 4use 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
75pub trait DocCommentsOwner: AstNode { 73pub 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
85impl CommentIter { 79impl 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] ":"