diff options
56 files changed, 2995 insertions, 2359 deletions
diff --git a/crates/hir/src/attrs.rs b/crates/hir/src/attrs.rs index b9c695921..dab8da7bb 100644 --- a/crates/hir/src/attrs.rs +++ b/crates/hir/src/attrs.rs | |||
@@ -11,8 +11,8 @@ use hir_ty::db::HirDatabase; | |||
11 | use syntax::ast; | 11 | use syntax::ast; |
12 | 12 | ||
13 | use crate::{ | 13 | use crate::{ |
14 | Adt, Const, ConstParam, Enum, Field, Function, GenericParam, LifetimeParam, MacroDef, Module, | 14 | Adt, Const, ConstParam, Enum, Field, Function, GenericParam, Impl, LifetimeParam, MacroDef, |
15 | ModuleDef, Static, Struct, Trait, TypeAlias, TypeParam, Union, Variant, | 15 | Module, ModuleDef, Static, Struct, Trait, TypeAlias, TypeParam, Union, Variant, |
16 | }; | 16 | }; |
17 | 17 | ||
18 | pub trait HasAttrs { | 18 | pub trait HasAttrs { |
@@ -64,6 +64,7 @@ impl_has_attrs![ | |||
64 | (Adt, AdtId), | 64 | (Adt, AdtId), |
65 | (Module, ModuleId), | 65 | (Module, ModuleId), |
66 | (GenericParam, GenericParamId), | 66 | (GenericParam, GenericParamId), |
67 | (Impl, ImplId), | ||
67 | ]; | 68 | ]; |
68 | 69 | ||
69 | macro_rules! impl_has_attrs_enum { | 70 | macro_rules! impl_has_attrs_enum { |
diff --git a/crates/hir/src/db.rs b/crates/hir/src/db.rs index d444f4bbb..1902a8d16 100644 --- a/crates/hir/src/db.rs +++ b/crates/hir/src/db.rs | |||
@@ -3,10 +3,10 @@ | |||
3 | pub use hir_def::db::{ | 3 | pub use hir_def::db::{ |
4 | AttrsQuery, BlockDefMapQuery, BodyQuery, BodyWithSourceMapQuery, ConstDataQuery, | 4 | AttrsQuery, BlockDefMapQuery, BodyQuery, BodyWithSourceMapQuery, ConstDataQuery, |
5 | CrateDefMapQueryQuery, CrateLangItemsQuery, DefDatabase, DefDatabaseStorage, EnumDataQuery, | 5 | CrateDefMapQueryQuery, CrateLangItemsQuery, DefDatabase, DefDatabaseStorage, EnumDataQuery, |
6 | ExprScopesQuery, FunctionDataQuery, GenericParamsQuery, ImplDataQuery, ImportMapQuery, | 6 | ExprScopesQuery, FileItemTreeQuery, FunctionDataQuery, GenericParamsQuery, ImplDataQuery, |
7 | InternConstQuery, InternDatabase, InternDatabaseStorage, InternEnumQuery, InternFunctionQuery, | 7 | ImportMapQuery, InternConstQuery, InternDatabase, InternDatabaseStorage, InternEnumQuery, |
8 | InternImplQuery, InternStaticQuery, InternStructQuery, InternTraitQuery, InternTypeAliasQuery, | 8 | InternFunctionQuery, InternImplQuery, InternStaticQuery, InternStructQuery, InternTraitQuery, |
9 | InternUnionQuery, ItemTreeQuery, LangItemQuery, StaticDataQuery, StructDataQuery, | 9 | InternTypeAliasQuery, InternUnionQuery, LangItemQuery, StaticDataQuery, StructDataQuery, |
10 | TraitDataQuery, TypeAliasDataQuery, UnionDataQuery, | 10 | TraitDataQuery, TypeAliasDataQuery, UnionDataQuery, |
11 | }; | 11 | }; |
12 | pub use hir_expand::db::{ | 12 | pub use hir_expand::db::{ |
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 861b7329e..b41a36a78 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs | |||
@@ -89,7 +89,7 @@ pub use crate::{ | |||
89 | pub use { | 89 | pub use { |
90 | hir_def::{ | 90 | hir_def::{ |
91 | adt::StructKind, | 91 | adt::StructKind, |
92 | attr::{Attrs, Documentation}, | 92 | attr::{Attr, Attrs, Documentation}, |
93 | body::scope::ExprScopes, | 93 | body::scope::ExprScopes, |
94 | find_path::PrefixKind, | 94 | find_path::PrefixKind, |
95 | import_map, | 95 | import_map, |
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index 00b076175..15651bb22 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs | |||
@@ -574,7 +574,7 @@ impl<'db> SemanticsImpl<'db> { | |||
574 | } | 574 | } |
575 | 575 | ||
576 | fn scope_for_def(&self, def: Trait) -> SemanticsScope<'db> { | 576 | fn scope_for_def(&self, def: Trait) -> SemanticsScope<'db> { |
577 | let file_id = self.db.lookup_intern_trait(def.id).id.file_id; | 577 | let file_id = self.db.lookup_intern_trait(def.id).id.file_id(); |
578 | let resolver = def.id.resolver(self.db.upcast()); | 578 | let resolver = def.id.resolver(self.db.upcast()); |
579 | SemanticsScope { db: self.db, file_id, resolver } | 579 | SemanticsScope { db: self.db, file_id, resolver } |
580 | } | 580 | } |
@@ -752,6 +752,7 @@ macro_rules! to_def_impls { | |||
752 | 752 | ||
753 | to_def_impls![ | 753 | to_def_impls![ |
754 | (crate::Module, ast::Module, module_to_def), | 754 | (crate::Module, ast::Module, module_to_def), |
755 | (crate::Module, ast::SourceFile, source_file_to_def), | ||
755 | (crate::Struct, ast::Struct, struct_to_def), | 756 | (crate::Struct, ast::Struct, struct_to_def), |
756 | (crate::Enum, ast::Enum, enum_to_def), | 757 | (crate::Enum, ast::Enum, enum_to_def), |
757 | (crate::Union, ast::Union, union_to_def), | 758 | (crate::Union, ast::Union, union_to_def), |
diff --git a/crates/hir/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs index e9d820140..c6ad5ecb5 100644 --- a/crates/hir/src/semantics/source_to_def.rs +++ b/crates/hir/src/semantics/source_to_def.rs | |||
@@ -71,6 +71,12 @@ impl SourceToDefCtx<'_, '_> { | |||
71 | Some(def_map.module_id(child_id)) | 71 | Some(def_map.module_id(child_id)) |
72 | } | 72 | } |
73 | 73 | ||
74 | pub(super) fn source_file_to_def(&mut self, src: InFile<ast::SourceFile>) -> Option<ModuleId> { | ||
75 | let _p = profile::span("source_file_to_def"); | ||
76 | let file_id = src.file_id.original_file(self.db.upcast()); | ||
77 | self.file_to_def(file_id).get(0).copied() | ||
78 | } | ||
79 | |||
74 | pub(super) fn trait_to_def(&mut self, src: InFile<ast::Trait>) -> Option<TraitId> { | 80 | pub(super) fn trait_to_def(&mut self, src: InFile<ast::Trait>) -> Option<TraitId> { |
75 | self.to_def(src, keys::TRAIT) | 81 | self.to_def(src, keys::TRAIT) |
76 | } | 82 | } |
diff --git a/crates/hir_def/src/adt.rs b/crates/hir_def/src/adt.rs index 1b9bb8235..58e35353b 100644 --- a/crates/hir_def/src/adt.rs +++ b/crates/hir_def/src/adt.rs | |||
@@ -94,7 +94,7 @@ impl StructData { | |||
94 | pub(crate) fn struct_data_query(db: &dyn DefDatabase, id: StructId) -> Arc<StructData> { | 94 | pub(crate) fn struct_data_query(db: &dyn DefDatabase, id: StructId) -> Arc<StructData> { |
95 | let loc = id.lookup(db); | 95 | let loc = id.lookup(db); |
96 | let krate = loc.container.krate; | 96 | let krate = loc.container.krate; |
97 | let item_tree = db.item_tree(loc.id.file_id); | 97 | let item_tree = loc.id.item_tree(db); |
98 | let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into()); | 98 | let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into()); |
99 | let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone(); | 99 | let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone(); |
100 | 100 | ||
@@ -110,7 +110,7 @@ impl StructData { | |||
110 | pub(crate) fn union_data_query(db: &dyn DefDatabase, id: UnionId) -> Arc<StructData> { | 110 | pub(crate) fn union_data_query(db: &dyn DefDatabase, id: UnionId) -> Arc<StructData> { |
111 | let loc = id.lookup(db); | 111 | let loc = id.lookup(db); |
112 | let krate = loc.container.krate; | 112 | let krate = loc.container.krate; |
113 | let item_tree = db.item_tree(loc.id.file_id); | 113 | let item_tree = loc.id.item_tree(db); |
114 | let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into()); | 114 | let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into()); |
115 | let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone(); | 115 | let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone(); |
116 | 116 | ||
@@ -130,7 +130,7 @@ impl EnumData { | |||
130 | pub(crate) fn enum_data_query(db: &dyn DefDatabase, e: EnumId) -> Arc<EnumData> { | 130 | pub(crate) fn enum_data_query(db: &dyn DefDatabase, e: EnumId) -> Arc<EnumData> { |
131 | let loc = e.lookup(db); | 131 | let loc = e.lookup(db); |
132 | let krate = loc.container.krate; | 132 | let krate = loc.container.krate; |
133 | let item_tree = db.item_tree(loc.id.file_id); | 133 | let item_tree = loc.id.item_tree(db); |
134 | let cfg_options = db.crate_graph()[krate].cfg_options.clone(); | 134 | let cfg_options = db.crate_graph()[krate].cfg_options.clone(); |
135 | 135 | ||
136 | let enum_ = &item_tree[loc.id.value]; | 136 | let enum_ = &item_tree[loc.id.value]; |
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs index b0b4b5052..e4c84afbf 100644 --- a/crates/hir_def/src/attr.rs +++ b/crates/hir_def/src/attr.rs | |||
@@ -76,37 +76,23 @@ impl ops::Deref for Attrs { | |||
76 | impl RawAttrs { | 76 | impl RawAttrs { |
77 | pub(crate) const EMPTY: Self = Self { entries: None }; | 77 | pub(crate) const EMPTY: Self = Self { entries: None }; |
78 | 78 | ||
79 | pub(crate) fn new(owner: &dyn AttrsOwner, hygiene: &Hygiene) -> Self { | 79 | pub(crate) fn new(owner: &dyn ast::AttrsOwner, hygiene: &Hygiene) -> Self { |
80 | let attrs: Vec<_> = collect_attrs(owner).collect(); | 80 | let entries = collect_attrs(owner) |
81 | let entries = if attrs.is_empty() { | 81 | .enumerate() |
82 | // Avoid heap allocation | 82 | .flat_map(|(i, attr)| match attr { |
83 | None | 83 | Either::Left(attr) => Attr::from_src(attr, hygiene, i as u32), |
84 | } else { | 84 | Either::Right(comment) => comment.doc_comment().map(|doc| Attr { |
85 | Some( | 85 | index: i as u32, |
86 | attrs | 86 | input: Some(AttrInput::Literal(SmolStr::new(doc))), |
87 | .into_iter() | 87 | path: ModPath::from(hir_expand::name!(doc)), |
88 | .enumerate() | 88 | }), |
89 | .flat_map(|(i, attr)| match attr { | 89 | }) |
90 | Either::Left(attr) => Attr::from_src(attr, hygiene).map(|attr| (i, attr)), | 90 | .collect::<Arc<_>>(); |
91 | Either::Right(comment) => comment.doc_comment().map(|doc| { | 91 | |
92 | ( | 92 | Self { entries: if entries.is_empty() { None } else { Some(entries) } } |
93 | i, | ||
94 | Attr { | ||
95 | index: 0, | ||
96 | input: Some(AttrInput::Literal(SmolStr::new(doc))), | ||
97 | path: ModPath::from(hir_expand::name!(doc)), | ||
98 | }, | ||
99 | ) | ||
100 | }), | ||
101 | }) | ||
102 | .map(|(i, attr)| Attr { index: i as u32, ..attr }) | ||
103 | .collect(), | ||
104 | ) | ||
105 | }; | ||
106 | Self { entries } | ||
107 | } | 93 | } |
108 | 94 | ||
109 | fn from_attrs_owner(db: &dyn DefDatabase, owner: InFile<&dyn AttrsOwner>) -> Self { | 95 | fn from_attrs_owner(db: &dyn DefDatabase, owner: InFile<&dyn ast::AttrsOwner>) -> Self { |
110 | let hygiene = Hygiene::new(db.upcast(), owner.file_id); | 96 | let hygiene = Hygiene::new(db.upcast(), owner.file_id); |
111 | Self::new(owner.value, &hygiene) | 97 | Self::new(owner.value, &hygiene) |
112 | } | 98 | } |
@@ -136,16 +122,15 @@ impl RawAttrs { | |||
136 | let new_attrs = self | 122 | let new_attrs = self |
137 | .iter() | 123 | .iter() |
138 | .flat_map(|attr| -> SmallVec<[_; 1]> { | 124 | .flat_map(|attr| -> SmallVec<[_; 1]> { |
139 | let attr = attr.clone(); | ||
140 | let is_cfg_attr = | 125 | let is_cfg_attr = |
141 | attr.path.as_ident().map_or(false, |name| *name == hir_expand::name![cfg_attr]); | 126 | attr.path.as_ident().map_or(false, |name| *name == hir_expand::name![cfg_attr]); |
142 | if !is_cfg_attr { | 127 | if !is_cfg_attr { |
143 | return smallvec![attr]; | 128 | return smallvec![attr.clone()]; |
144 | } | 129 | } |
145 | 130 | ||
146 | let subtree = match &attr.input { | 131 | let subtree = match &attr.input { |
147 | Some(AttrInput::TokenTree(it)) => it, | 132 | Some(AttrInput::TokenTree(it)) => it, |
148 | _ => return smallvec![attr], | 133 | _ => return smallvec![attr.clone()], |
149 | }; | 134 | }; |
150 | 135 | ||
151 | // Input subtree is: `(cfg, $(attr),+)` | 136 | // Input subtree is: `(cfg, $(attr),+)` |
@@ -157,11 +142,13 @@ impl RawAttrs { | |||
157 | let cfg = parts.next().unwrap(); | 142 | let cfg = parts.next().unwrap(); |
158 | let cfg = Subtree { delimiter: subtree.delimiter, token_trees: cfg.to_vec() }; | 143 | let cfg = Subtree { delimiter: subtree.delimiter, token_trees: cfg.to_vec() }; |
159 | let cfg = CfgExpr::parse(&cfg); | 144 | let cfg = CfgExpr::parse(&cfg); |
145 | let index = attr.index; | ||
160 | let attrs = parts.filter(|a| !a.is_empty()).filter_map(|attr| { | 146 | let attrs = parts.filter(|a| !a.is_empty()).filter_map(|attr| { |
161 | let tree = Subtree { delimiter: None, token_trees: attr.to_vec() }; | 147 | let tree = Subtree { delimiter: None, token_trees: attr.to_vec() }; |
162 | let attr = ast::Attr::parse(&format!("#[{}]", tree)).ok()?; | 148 | let attr = ast::Attr::parse(&format!("#[{}]", tree)).ok()?; |
163 | let hygiene = Hygiene::new_unhygienic(); // FIXME | 149 | // FIXME hygiene |
164 | Attr::from_src(attr, &hygiene) | 150 | let hygiene = Hygiene::new_unhygienic(); |
151 | Attr::from_src(attr, &hygiene, index) | ||
165 | }); | 152 | }); |
166 | 153 | ||
167 | let cfg_options = &crate_graph[krate].cfg_options; | 154 | let cfg_options = &crate_graph[krate].cfg_options; |
@@ -191,7 +178,7 @@ impl Attrs { | |||
191 | Some(it) => { | 178 | Some(it) => { |
192 | let raw_attrs = RawAttrs::from_attrs_owner( | 179 | let raw_attrs = RawAttrs::from_attrs_owner( |
193 | db, | 180 | db, |
194 | it.as_ref().map(|it| it as &dyn AttrsOwner), | 181 | it.as_ref().map(|it| it as &dyn ast::AttrsOwner), |
195 | ); | 182 | ); |
196 | match mod_data.definition_source(db) { | 183 | match mod_data.definition_source(db) { |
197 | InFile { file_id, value: ModuleSource::SourceFile(file) } => raw_attrs | 184 | InFile { file_id, value: ModuleSource::SourceFile(file) } => raw_attrs |
@@ -202,9 +189,9 @@ impl Attrs { | |||
202 | None => RawAttrs::from_attrs_owner( | 189 | None => RawAttrs::from_attrs_owner( |
203 | db, | 190 | db, |
204 | mod_data.definition_source(db).as_ref().map(|src| match src { | 191 | mod_data.definition_source(db).as_ref().map(|src| match src { |
205 | ModuleSource::SourceFile(file) => file as &dyn AttrsOwner, | 192 | ModuleSource::SourceFile(file) => file as &dyn ast::AttrsOwner, |
206 | ModuleSource::Module(module) => module as &dyn AttrsOwner, | 193 | ModuleSource::Module(module) => module as &dyn ast::AttrsOwner, |
207 | ModuleSource::BlockExpr(block) => block as &dyn AttrsOwner, | 194 | ModuleSource::BlockExpr(block) => block as &dyn ast::AttrsOwner, |
208 | }), | 195 | }), |
209 | ), | 196 | ), |
210 | } | 197 | } |
@@ -262,7 +249,7 @@ impl Attrs { | |||
262 | let mut res = ArenaMap::default(); | 249 | let mut res = ArenaMap::default(); |
263 | 250 | ||
264 | for (id, var) in src.value.iter() { | 251 | for (id, var) in src.value.iter() { |
265 | let attrs = RawAttrs::from_attrs_owner(db, src.with_value(var as &dyn AttrsOwner)) | 252 | let attrs = RawAttrs::from_attrs_owner(db, src.with_value(var as &dyn ast::AttrsOwner)) |
266 | .filter(db, krate); | 253 | .filter(db, krate); |
267 | 254 | ||
268 | res.insert(id, attrs) | 255 | res.insert(id, attrs) |
@@ -293,6 +280,13 @@ impl Attrs { | |||
293 | Arc::new(res) | 280 | Arc::new(res) |
294 | } | 281 | } |
295 | 282 | ||
283 | /// Constructs a map that maps the lowered `Attr`s in this `Attrs` back to its original syntax nodes. | ||
284 | /// | ||
285 | /// `owner` must be the original owner of the attributes. | ||
286 | pub fn source_map(&self, owner: &dyn ast::AttrsOwner) -> AttrSourceMap { | ||
287 | AttrSourceMap { attrs: collect_attrs(owner).collect() } | ||
288 | } | ||
289 | |||
296 | pub fn by_key(&self, key: &'static str) -> AttrQuery<'_> { | 290 | pub fn by_key(&self, key: &'static str) -> AttrQuery<'_> { |
297 | AttrQuery { attrs: self, key } | 291 | AttrQuery { attrs: self, key } |
298 | } | 292 | } |
@@ -317,15 +311,34 @@ impl Attrs { | |||
317 | AttrInput::Literal(s) => Some(s), | 311 | AttrInput::Literal(s) => Some(s), |
318 | AttrInput::TokenTree(_) => None, | 312 | AttrInput::TokenTree(_) => None, |
319 | }); | 313 | }); |
320 | // FIXME: Replace `Itertools::intersperse` with `Iterator::intersperse[_with]` until the | 314 | let indent = docs |
321 | // libstd api gets stabilized (https://github.com/rust-lang/rust/issues/79524). | 315 | .clone() |
322 | let docs = Itertools::intersperse(docs, &SmolStr::new_inline("\n")) | 316 | .flat_map(|s| s.lines()) |
323 | .map(|it| it.as_str()) | 317 | .filter(|line| !line.chars().all(|c| c.is_whitespace())) |
324 | .collect::<String>(); | 318 | .map(|line| line.chars().take_while(|c| c.is_whitespace()).count()) |
325 | if docs.is_empty() { | 319 | .min() |
320 | .unwrap_or(0); | ||
321 | let mut buf = String::new(); | ||
322 | for doc in docs { | ||
323 | // str::lines doesn't yield anything for the empty string | ||
324 | if !doc.is_empty() { | ||
325 | buf.extend(Itertools::intersperse( | ||
326 | doc.lines().map(|line| { | ||
327 | line.char_indices() | ||
328 | .nth(indent) | ||
329 | .map_or(line, |(offset, _)| &line[offset..]) | ||
330 | .trim_end() | ||
331 | }), | ||
332 | "\n", | ||
333 | )); | ||
334 | } | ||
335 | buf.push('\n'); | ||
336 | } | ||
337 | buf.pop(); | ||
338 | if buf.is_empty() { | ||
326 | None | 339 | None |
327 | } else { | 340 | } else { |
328 | Some(Documentation(docs)) | 341 | Some(Documentation(buf)) |
329 | } | 342 | } |
330 | } | 343 | } |
331 | } | 344 | } |
@@ -365,6 +378,24 @@ fn inner_attributes( | |||
365 | Some((attrs, docs)) | 378 | Some((attrs, docs)) |
366 | } | 379 | } |
367 | 380 | ||
381 | pub struct AttrSourceMap { | ||
382 | attrs: Vec<Either<ast::Attr, ast::Comment>>, | ||
383 | } | ||
384 | |||
385 | impl AttrSourceMap { | ||
386 | /// Maps the lowered `Attr` back to its original syntax node. | ||
387 | /// | ||
388 | /// `attr` must come from the `owner` used for AttrSourceMap | ||
389 | /// | ||
390 | /// Note that the returned syntax node might be a `#[cfg_attr]`, or a doc comment, instead of | ||
391 | /// the attribute represented by `Attr`. | ||
392 | pub fn source_of(&self, attr: &Attr) -> &Either<ast::Attr, ast::Comment> { | ||
393 | self.attrs | ||
394 | .get(attr.index as usize) | ||
395 | .unwrap_or_else(|| panic!("cannot find `Attr` at index {}", attr.index)) | ||
396 | } | ||
397 | } | ||
398 | |||
368 | #[derive(Debug, Clone, PartialEq, Eq)] | 399 | #[derive(Debug, Clone, PartialEq, Eq)] |
369 | pub struct Attr { | 400 | pub struct Attr { |
370 | index: u32, | 401 | index: u32, |
@@ -381,7 +412,7 @@ pub enum AttrInput { | |||
381 | } | 412 | } |
382 | 413 | ||
383 | impl Attr { | 414 | impl Attr { |
384 | fn from_src(ast: ast::Attr, hygiene: &Hygiene) -> Option<Attr> { | 415 | fn from_src(ast: ast::Attr, hygiene: &Hygiene, index: u32) -> Option<Attr> { |
385 | let path = ModPath::from_src(ast.path()?, hygiene)?; | 416 | let path = ModPath::from_src(ast.path()?, hygiene)?; |
386 | let input = if let Some(lit) = ast.literal() { | 417 | let input = if let Some(lit) = ast.literal() { |
387 | let value = match lit.kind() { | 418 | let value = match lit.kind() { |
@@ -394,7 +425,7 @@ impl Attr { | |||
394 | } else { | 425 | } else { |
395 | None | 426 | None |
396 | }; | 427 | }; |
397 | Some(Attr { index: 0, path, input }) | 428 | Some(Attr { index, path, input }) |
398 | } | 429 | } |
399 | 430 | ||
400 | /// Maps this lowered `Attr` back to its original syntax node. | 431 | /// Maps this lowered `Attr` back to its original syntax node. |
@@ -403,7 +434,7 @@ impl Attr { | |||
403 | /// | 434 | /// |
404 | /// Note that the returned syntax node might be a `#[cfg_attr]`, or a doc comment, instead of | 435 | /// Note that the returned syntax node might be a `#[cfg_attr]`, or a doc comment, instead of |
405 | /// the attribute represented by `Attr`. | 436 | /// the attribute represented by `Attr`. |
406 | pub fn to_src(&self, owner: &dyn AttrsOwner) -> Either<ast::Attr, ast::Comment> { | 437 | pub fn to_src(&self, owner: &dyn ast::AttrsOwner) -> Either<ast::Attr, ast::Comment> { |
407 | collect_attrs(owner).nth(self.index as usize).unwrap_or_else(|| { | 438 | collect_attrs(owner).nth(self.index as usize).unwrap_or_else(|| { |
408 | panic!("cannot find `Attr` at index {} in {}", self.index, owner.syntax()) | 439 | panic!("cannot find `Attr` at index {} in {}", self.index, owner.syntax()) |
409 | }) | 440 | }) |
@@ -448,6 +479,13 @@ impl Attr { | |||
448 | _ => None, | 479 | _ => None, |
449 | } | 480 | } |
450 | } | 481 | } |
482 | |||
483 | pub fn string_value(&self) -> Option<&SmolStr> { | ||
484 | match self.input.as_ref()? { | ||
485 | AttrInput::Literal(it) => Some(it), | ||
486 | _ => None, | ||
487 | } | ||
488 | } | ||
451 | } | 489 | } |
452 | 490 | ||
453 | #[derive(Debug, Clone, Copy)] | 491 | #[derive(Debug, Clone, Copy)] |
@@ -475,7 +513,7 @@ impl<'a> AttrQuery<'a> { | |||
475 | self.attrs().next().is_some() | 513 | self.attrs().next().is_some() |
476 | } | 514 | } |
477 | 515 | ||
478 | pub(crate) fn attrs(self) -> impl Iterator<Item = &'a Attr> { | 516 | pub fn attrs(self) -> impl Iterator<Item = &'a Attr> + Clone { |
479 | let key = self.key; | 517 | let key = self.key; |
480 | self.attrs | 518 | self.attrs |
481 | .iter() | 519 | .iter() |
@@ -488,16 +526,18 @@ where | |||
488 | N: ast::AttrsOwner, | 526 | N: ast::AttrsOwner, |
489 | { | 527 | { |
490 | let src = InFile::new(src.file_id, src.to_node(db.upcast())); | 528 | let src = InFile::new(src.file_id, src.to_node(db.upcast())); |
491 | RawAttrs::from_attrs_owner(db, src.as_ref().map(|it| it as &dyn AttrsOwner)) | 529 | RawAttrs::from_attrs_owner(db, src.as_ref().map(|it| it as &dyn ast::AttrsOwner)) |
492 | } | 530 | } |
493 | 531 | ||
494 | fn attrs_from_item_tree<N: ItemTreeNode>(id: ItemTreeId<N>, db: &dyn DefDatabase) -> RawAttrs { | 532 | fn attrs_from_item_tree<N: ItemTreeNode>(id: ItemTreeId<N>, db: &dyn DefDatabase) -> RawAttrs { |
495 | let tree = db.item_tree(id.file_id); | 533 | let tree = id.item_tree(db); |
496 | let mod_item = N::id_to_mod_item(id.value); | 534 | let mod_item = N::id_to_mod_item(id.value); |
497 | tree.raw_attrs(mod_item.into()).clone() | 535 | tree.raw_attrs(mod_item.into()).clone() |
498 | } | 536 | } |
499 | 537 | ||
500 | fn collect_attrs(owner: &dyn AttrsOwner) -> impl Iterator<Item = Either<ast::Attr, ast::Comment>> { | 538 | fn collect_attrs( |
539 | owner: &dyn ast::AttrsOwner, | ||
540 | ) -> impl Iterator<Item = Either<ast::Attr, ast::Comment>> { | ||
501 | let (inner_attrs, inner_docs) = inner_attributes(owner.syntax()) | 541 | let (inner_attrs, inner_docs) = inner_attributes(owner.syntax()) |
502 | .map_or((None, None), |(attrs, docs)| ((Some(attrs), Some(docs)))); | 542 | .map_or((None, None), |(attrs, docs)| ((Some(attrs), Some(docs)))); |
503 | 543 | ||
diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs index 60b25db56..19f5065d1 100644 --- a/crates/hir_def/src/body/lower.rs +++ b/crates/hir_def/src/body/lower.rs | |||
@@ -177,12 +177,15 @@ impl ExprCollector<'_> { | |||
177 | } | 177 | } |
178 | 178 | ||
179 | fn collect_expr(&mut self, expr: ast::Expr) -> ExprId { | 179 | fn collect_expr(&mut self, expr: ast::Expr) -> ExprId { |
180 | self.maybe_collect_expr(expr).unwrap_or_else(|| self.missing_expr()) | ||
181 | } | ||
182 | |||
183 | /// Returns `None` if the expression is `#[cfg]`d out. | ||
184 | fn maybe_collect_expr(&mut self, expr: ast::Expr) -> Option<ExprId> { | ||
180 | let syntax_ptr = AstPtr::new(&expr); | 185 | let syntax_ptr = AstPtr::new(&expr); |
181 | if self.check_cfg(&expr).is_none() { | 186 | self.check_cfg(&expr)?; |
182 | return self.missing_expr(); | ||
183 | } | ||
184 | 187 | ||
185 | match expr { | 188 | Some(match expr { |
186 | ast::Expr::IfExpr(e) => { | 189 | ast::Expr::IfExpr(e) => { |
187 | let then_branch = self.collect_block_opt(e.then_branch()); | 190 | let then_branch = self.collect_block_opt(e.then_branch()); |
188 | 191 | ||
@@ -211,8 +214,9 @@ impl ExprCollector<'_> { | |||
211 | guard: None, | 214 | guard: None, |
212 | }, | 215 | }, |
213 | ]; | 216 | ]; |
214 | return self | 217 | return Some( |
215 | .alloc_expr(Expr::Match { expr: match_expr, arms }, syntax_ptr); | 218 | self.alloc_expr(Expr::Match { expr: match_expr, arms }, syntax_ptr), |
219 | ); | ||
216 | } | 220 | } |
217 | }, | 221 | }, |
218 | }; | 222 | }; |
@@ -283,8 +287,9 @@ impl ExprCollector<'_> { | |||
283 | ]; | 287 | ]; |
284 | let match_expr = | 288 | let match_expr = |
285 | self.alloc_expr_desugared(Expr::Match { expr: match_expr, arms }); | 289 | self.alloc_expr_desugared(Expr::Match { expr: match_expr, arms }); |
286 | return self | 290 | return Some( |
287 | .alloc_expr(Expr::Loop { body: match_expr, label }, syntax_ptr); | 291 | self.alloc_expr(Expr::Loop { body: match_expr, label }, syntax_ptr), |
292 | ); | ||
288 | } | 293 | } |
289 | }, | 294 | }, |
290 | }; | 295 | }; |
@@ -301,7 +306,7 @@ impl ExprCollector<'_> { | |||
301 | ast::Expr::CallExpr(e) => { | 306 | ast::Expr::CallExpr(e) => { |
302 | let callee = self.collect_expr_opt(e.expr()); | 307 | let callee = self.collect_expr_opt(e.expr()); |
303 | let args = if let Some(arg_list) = e.arg_list() { | 308 | let args = if let Some(arg_list) = e.arg_list() { |
304 | arg_list.args().map(|e| self.collect_expr(e)).collect() | 309 | arg_list.args().filter_map(|e| self.maybe_collect_expr(e)).collect() |
305 | } else { | 310 | } else { |
306 | Vec::new() | 311 | Vec::new() |
307 | }; | 312 | }; |
@@ -310,7 +315,7 @@ impl ExprCollector<'_> { | |||
310 | ast::Expr::MethodCallExpr(e) => { | 315 | ast::Expr::MethodCallExpr(e) => { |
311 | let receiver = self.collect_expr_opt(e.receiver()); | 316 | let receiver = self.collect_expr_opt(e.receiver()); |
312 | let args = if let Some(arg_list) = e.arg_list() { | 317 | let args = if let Some(arg_list) = e.arg_list() { |
313 | arg_list.args().map(|e| self.collect_expr(e)).collect() | 318 | arg_list.args().filter_map(|e| self.maybe_collect_expr(e)).collect() |
314 | } else { | 319 | } else { |
315 | Vec::new() | 320 | Vec::new() |
316 | }; | 321 | }; |
@@ -538,7 +543,7 @@ impl ExprCollector<'_> { | |||
538 | self.alloc_expr(Expr::Missing, syntax_ptr) | 543 | self.alloc_expr(Expr::Missing, syntax_ptr) |
539 | } | 544 | } |
540 | } | 545 | } |
541 | } | 546 | }) |
542 | } | 547 | } |
543 | 548 | ||
544 | fn collect_macro_call<F: FnMut(&mut Self, Option<T>), T: ast::AstNode>( | 549 | fn collect_macro_call<F: FnMut(&mut Self, Option<T>), T: ast::AstNode>( |
diff --git a/crates/hir_def/src/body/tests.rs b/crates/hir_def/src/body/tests.rs index f8e6f70e8..faa133297 100644 --- a/crates/hir_def/src/body/tests.rs +++ b/crates/hir_def/src/body/tests.rs | |||
@@ -137,7 +137,7 @@ fn f() { | |||
137 | include!(invalid); | 137 | include!(invalid); |
138 | //^^^^^^^^^^^^^^^^^ could not convert tokens | 138 | //^^^^^^^^^^^^^^^^^ could not convert tokens |
139 | include!("does not exist"); | 139 | include!("does not exist"); |
140 | //^^^^^^^^^^^^^^^^^^^^^^^^^^ could not convert tokens | 140 | //^^^^^^^^^^^^^^^^^^^^^^^^^^ failed to load file `does not exist` |
141 | 141 | ||
142 | env!(invalid); | 142 | env!(invalid); |
143 | //^^^^^^^^^^^^^ could not convert tokens | 143 | //^^^^^^^^^^^^^ could not convert tokens |
diff --git a/crates/hir_def/src/data.rs b/crates/hir_def/src/data.rs index 1a27f7bf2..0be868ba2 100644 --- a/crates/hir_def/src/data.rs +++ b/crates/hir_def/src/data.rs | |||
@@ -9,7 +9,7 @@ use crate::{ | |||
9 | attr::Attrs, | 9 | attr::Attrs, |
10 | body::Expander, | 10 | body::Expander, |
11 | db::DefDatabase, | 11 | db::DefDatabase, |
12 | item_tree::{AssocItem, FunctionQualifier, ItemTreeId, ModItem}, | 12 | item_tree::{AssocItem, FunctionQualifier, ItemTreeId, ModItem, Param}, |
13 | type_ref::{TypeBound, TypeRef}, | 13 | type_ref::{TypeBound, TypeRef}, |
14 | visibility::RawVisibility, | 14 | visibility::RawVisibility, |
15 | AssocContainerId, AssocItemId, ConstId, ConstLoc, FunctionId, FunctionLoc, HasModule, ImplId, | 15 | AssocContainerId, AssocItemId, ConstId, ConstLoc, FunctionId, FunctionLoc, HasModule, ImplId, |
@@ -36,19 +36,38 @@ impl FunctionData { | |||
36 | pub(crate) fn fn_data_query(db: &dyn DefDatabase, func: FunctionId) -> Arc<FunctionData> { | 36 | pub(crate) fn fn_data_query(db: &dyn DefDatabase, func: FunctionId) -> Arc<FunctionData> { |
37 | let loc = func.lookup(db); | 37 | let loc = func.lookup(db); |
38 | let krate = loc.container.module(db).krate; | 38 | let krate = loc.container.module(db).krate; |
39 | let item_tree = db.item_tree(loc.id.file_id); | 39 | let crate_graph = db.crate_graph(); |
40 | let cfg_options = &crate_graph[krate].cfg_options; | ||
41 | let item_tree = loc.id.item_tree(db); | ||
40 | let func = &item_tree[loc.id.value]; | 42 | let func = &item_tree[loc.id.value]; |
41 | 43 | ||
44 | let enabled_params = func | ||
45 | .params | ||
46 | .clone() | ||
47 | .filter(|¶m| item_tree.attrs(db, krate, param.into()).is_cfg_enabled(cfg_options)); | ||
48 | |||
49 | // If last cfg-enabled param is a `...` param, it's a varargs function. | ||
50 | let is_varargs = enabled_params | ||
51 | .clone() | ||
52 | .next_back() | ||
53 | .map_or(false, |param| matches!(item_tree[param], Param::Varargs)); | ||
54 | |||
42 | Arc::new(FunctionData { | 55 | Arc::new(FunctionData { |
43 | name: func.name.clone(), | 56 | name: func.name.clone(), |
44 | params: func.params.iter().map(|id| item_tree[*id].clone()).collect(), | 57 | params: enabled_params |
58 | .clone() | ||
59 | .filter_map(|id| match &item_tree[id] { | ||
60 | Param::Normal(ty) => Some(item_tree[*ty].clone()), | ||
61 | Param::Varargs => None, | ||
62 | }) | ||
63 | .collect(), | ||
45 | ret_type: item_tree[func.ret_type].clone(), | 64 | ret_type: item_tree[func.ret_type].clone(), |
46 | attrs: item_tree.attrs(db, krate, ModItem::from(loc.id.value).into()), | 65 | attrs: item_tree.attrs(db, krate, ModItem::from(loc.id.value).into()), |
47 | has_self_param: func.has_self_param, | 66 | has_self_param: func.has_self_param, |
48 | has_body: func.has_body, | 67 | has_body: func.has_body, |
49 | qualifier: func.qualifier.clone(), | 68 | qualifier: func.qualifier.clone(), |
50 | is_in_extern_block: func.is_in_extern_block, | 69 | is_in_extern_block: func.is_in_extern_block, |
51 | is_varargs: func.is_varargs, | 70 | is_varargs, |
52 | visibility: item_tree[func.visibility].clone(), | 71 | visibility: item_tree[func.visibility].clone(), |
53 | }) | 72 | }) |
54 | } | 73 | } |
@@ -70,7 +89,7 @@ impl TypeAliasData { | |||
70 | typ: TypeAliasId, | 89 | typ: TypeAliasId, |
71 | ) -> Arc<TypeAliasData> { | 90 | ) -> Arc<TypeAliasData> { |
72 | let loc = typ.lookup(db); | 91 | let loc = typ.lookup(db); |
73 | let item_tree = db.item_tree(loc.id.file_id); | 92 | let item_tree = loc.id.item_tree(db); |
74 | let typ = &item_tree[loc.id.value]; | 93 | let typ = &item_tree[loc.id.value]; |
75 | 94 | ||
76 | Arc::new(TypeAliasData { | 95 | Arc::new(TypeAliasData { |
@@ -96,23 +115,23 @@ pub struct TraitData { | |||
96 | impl TraitData { | 115 | impl TraitData { |
97 | pub(crate) fn trait_data_query(db: &dyn DefDatabase, tr: TraitId) -> Arc<TraitData> { | 116 | pub(crate) fn trait_data_query(db: &dyn DefDatabase, tr: TraitId) -> Arc<TraitData> { |
98 | let tr_loc = tr.lookup(db); | 117 | let tr_loc = tr.lookup(db); |
99 | let item_tree = db.item_tree(tr_loc.id.file_id); | 118 | let item_tree = tr_loc.id.item_tree(db); |
100 | let tr_def = &item_tree[tr_loc.id.value]; | 119 | let tr_def = &item_tree[tr_loc.id.value]; |
101 | let name = tr_def.name.clone(); | 120 | let name = tr_def.name.clone(); |
102 | let is_auto = tr_def.is_auto; | 121 | let is_auto = tr_def.is_auto; |
103 | let is_unsafe = tr_def.is_unsafe; | 122 | let is_unsafe = tr_def.is_unsafe; |
104 | let module_id = tr_loc.container; | 123 | let module_id = tr_loc.container; |
105 | let container = AssocContainerId::TraitId(tr); | 124 | let container = AssocContainerId::TraitId(tr); |
106 | let mut expander = Expander::new(db, tr_loc.id.file_id, module_id); | ||
107 | let visibility = item_tree[tr_def.visibility].clone(); | 125 | let visibility = item_tree[tr_def.visibility].clone(); |
108 | let bounds = tr_def.bounds.clone(); | 126 | let bounds = tr_def.bounds.clone(); |
127 | let mut expander = Expander::new(db, tr_loc.id.file_id(), module_id); | ||
109 | 128 | ||
110 | let items = collect_items( | 129 | let items = collect_items( |
111 | db, | 130 | db, |
112 | module_id, | 131 | module_id, |
113 | &mut expander, | 132 | &mut expander, |
114 | tr_def.items.iter().copied(), | 133 | tr_def.items.iter().copied(), |
115 | tr_loc.id.file_id, | 134 | tr_loc.id.file_id(), |
116 | container, | 135 | container, |
117 | 100, | 136 | 100, |
118 | ); | 137 | ); |
@@ -148,21 +167,21 @@ impl ImplData { | |||
148 | let _p = profile::span("impl_data_query"); | 167 | let _p = profile::span("impl_data_query"); |
149 | let impl_loc = id.lookup(db); | 168 | let impl_loc = id.lookup(db); |
150 | 169 | ||
151 | let item_tree = db.item_tree(impl_loc.id.file_id); | 170 | let item_tree = impl_loc.id.item_tree(db); |
152 | let impl_def = &item_tree[impl_loc.id.value]; | 171 | let impl_def = &item_tree[impl_loc.id.value]; |
153 | let target_trait = impl_def.target_trait.map(|id| item_tree[id].clone()); | 172 | let target_trait = impl_def.target_trait.map(|id| item_tree[id].clone()); |
154 | let target_type = item_tree[impl_def.target_type].clone(); | 173 | let target_type = item_tree[impl_def.target_type].clone(); |
155 | let is_negative = impl_def.is_negative; | 174 | let is_negative = impl_def.is_negative; |
156 | let module_id = impl_loc.container; | 175 | let module_id = impl_loc.container; |
157 | let container = AssocContainerId::ImplId(id); | 176 | let container = AssocContainerId::ImplId(id); |
158 | let mut expander = Expander::new(db, impl_loc.id.file_id, module_id); | 177 | let mut expander = Expander::new(db, impl_loc.id.file_id(), module_id); |
159 | 178 | ||
160 | let items = collect_items( | 179 | let items = collect_items( |
161 | db, | 180 | db, |
162 | module_id, | 181 | module_id, |
163 | &mut expander, | 182 | &mut expander, |
164 | impl_def.items.iter().copied(), | 183 | impl_def.items.iter().copied(), |
165 | impl_loc.id.file_id, | 184 | impl_loc.id.file_id(), |
166 | container, | 185 | container, |
167 | 100, | 186 | 100, |
168 | ); | 187 | ); |
@@ -183,7 +202,7 @@ pub struct ConstData { | |||
183 | impl ConstData { | 202 | impl ConstData { |
184 | pub(crate) fn const_data_query(db: &dyn DefDatabase, konst: ConstId) -> Arc<ConstData> { | 203 | pub(crate) fn const_data_query(db: &dyn DefDatabase, konst: ConstId) -> Arc<ConstData> { |
185 | let loc = konst.lookup(db); | 204 | let loc = konst.lookup(db); |
186 | let item_tree = db.item_tree(loc.id.file_id); | 205 | let item_tree = loc.id.item_tree(db); |
187 | let konst = &item_tree[loc.id.value]; | 206 | let konst = &item_tree[loc.id.value]; |
188 | 207 | ||
189 | Arc::new(ConstData { | 208 | Arc::new(ConstData { |
@@ -206,7 +225,7 @@ pub struct StaticData { | |||
206 | impl StaticData { | 225 | impl StaticData { |
207 | pub(crate) fn static_data_query(db: &dyn DefDatabase, konst: StaticId) -> Arc<StaticData> { | 226 | pub(crate) fn static_data_query(db: &dyn DefDatabase, konst: StaticId) -> Arc<StaticData> { |
208 | let node = konst.lookup(db); | 227 | let node = konst.lookup(db); |
209 | let item_tree = db.item_tree(node.id.file_id); | 228 | let item_tree = node.id.item_tree(db); |
210 | let statik = &item_tree[node.id.value]; | 229 | let statik = &item_tree[node.id.value]; |
211 | 230 | ||
212 | Arc::new(StaticData { | 231 | Arc::new(StaticData { |
@@ -232,22 +251,23 @@ fn collect_items( | |||
232 | return Vec::new(); | 251 | return Vec::new(); |
233 | } | 252 | } |
234 | 253 | ||
235 | let item_tree = db.item_tree(file_id); | 254 | let item_tree = db.file_item_tree(file_id); |
236 | let cfg_options = db.crate_graph()[module.krate].cfg_options.clone(); | 255 | let crate_graph = db.crate_graph(); |
256 | let cfg_options = &crate_graph[module.krate].cfg_options; | ||
237 | 257 | ||
238 | let mut items = Vec::new(); | 258 | let mut items = Vec::new(); |
239 | for item in assoc_items { | 259 | for item in assoc_items { |
260 | let attrs = item_tree.attrs(db, module.krate, ModItem::from(item).into()); | ||
261 | if !attrs.is_cfg_enabled(cfg_options) { | ||
262 | continue; | ||
263 | } | ||
264 | |||
240 | match item { | 265 | match item { |
241 | AssocItem::Function(id) => { | 266 | AssocItem::Function(id) => { |
242 | let item = &item_tree[id]; | 267 | let item = &item_tree[id]; |
243 | let attrs = item_tree.attrs(db, module.krate, ModItem::from(id).into()); | ||
244 | if !attrs.is_cfg_enabled(&cfg_options) { | ||
245 | continue; | ||
246 | } | ||
247 | let def = FunctionLoc { container, id: ItemTreeId::new(file_id, id) }.intern(db); | 268 | let def = FunctionLoc { container, id: ItemTreeId::new(file_id, id) }.intern(db); |
248 | items.push((item.name.clone(), def.into())); | 269 | items.push((item.name.clone(), def.into())); |
249 | } | 270 | } |
250 | // FIXME: cfg? | ||
251 | AssocItem::Const(id) => { | 271 | AssocItem::Const(id) => { |
252 | let item = &item_tree[id]; | 272 | let item = &item_tree[id]; |
253 | let name = match item.name.clone() { | 273 | let name = match item.name.clone() { |
@@ -272,7 +292,7 @@ fn collect_items( | |||
272 | if let Ok(res) = res { | 292 | if let Ok(res) = res { |
273 | if let Some((mark, mac)) = res.value { | 293 | if let Some((mark, mac)) = res.value { |
274 | let src: InFile<ast::MacroItems> = expander.to_source(mac); | 294 | let src: InFile<ast::MacroItems> = expander.to_source(mac); |
275 | let item_tree = db.item_tree(src.file_id); | 295 | let item_tree = db.file_item_tree(src.file_id); |
276 | let iter = | 296 | let iter = |
277 | item_tree.top_level_items().iter().filter_map(ModItem::as_assoc_item); | 297 | item_tree.top_level_items().iter().filter_map(ModItem::as_assoc_item); |
278 | items.extend(collect_items( | 298 | items.extend(collect_items( |
diff --git a/crates/hir_def/src/db.rs b/crates/hir_def/src/db.rs index cca5a086b..276caf5b3 100644 --- a/crates/hir_def/src/db.rs +++ b/crates/hir_def/src/db.rs | |||
@@ -48,8 +48,8 @@ pub trait InternDatabase: SourceDatabase { | |||
48 | 48 | ||
49 | #[salsa::query_group(DefDatabaseStorage)] | 49 | #[salsa::query_group(DefDatabaseStorage)] |
50 | pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> { | 50 | pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> { |
51 | #[salsa::invoke(ItemTree::item_tree_query)] | 51 | #[salsa::invoke(ItemTree::file_item_tree_query)] |
52 | fn item_tree(&self, file_id: HirFileId) -> Arc<ItemTree>; | 52 | fn file_item_tree(&self, file_id: HirFileId) -> Arc<ItemTree>; |
53 | 53 | ||
54 | #[salsa::invoke(crate_def_map_wait)] | 54 | #[salsa::invoke(crate_def_map_wait)] |
55 | #[salsa::transparent] | 55 | #[salsa::transparent] |
diff --git a/crates/hir_def/src/generics.rs b/crates/hir_def/src/generics.rs index a056ab797..7c6cbff11 100644 --- a/crates/hir_def/src/generics.rs +++ b/crates/hir_def/src/generics.rs | |||
@@ -97,43 +97,43 @@ impl GenericParams { | |||
97 | let generics = match def { | 97 | let generics = match def { |
98 | GenericDefId::FunctionId(id) => { | 98 | GenericDefId::FunctionId(id) => { |
99 | let id = id.lookup(db).id; | 99 | let id = id.lookup(db).id; |
100 | let tree = db.item_tree(id.file_id); | 100 | let tree = id.item_tree(db); |
101 | let item = &tree[id.value]; | 101 | let item = &tree[id.value]; |
102 | tree[item.generic_params].clone() | 102 | tree[item.generic_params].clone() |
103 | } | 103 | } |
104 | GenericDefId::AdtId(AdtId::StructId(id)) => { | 104 | GenericDefId::AdtId(AdtId::StructId(id)) => { |
105 | let id = id.lookup(db).id; | 105 | let id = id.lookup(db).id; |
106 | let tree = db.item_tree(id.file_id); | 106 | let tree = id.item_tree(db); |
107 | let item = &tree[id.value]; | 107 | let item = &tree[id.value]; |
108 | tree[item.generic_params].clone() | 108 | tree[item.generic_params].clone() |
109 | } | 109 | } |
110 | GenericDefId::AdtId(AdtId::EnumId(id)) => { | 110 | GenericDefId::AdtId(AdtId::EnumId(id)) => { |
111 | let id = id.lookup(db).id; | 111 | let id = id.lookup(db).id; |
112 | let tree = db.item_tree(id.file_id); | 112 | let tree = id.item_tree(db); |
113 | let item = &tree[id.value]; | 113 | let item = &tree[id.value]; |
114 | tree[item.generic_params].clone() | 114 | tree[item.generic_params].clone() |
115 | } | 115 | } |
116 | GenericDefId::AdtId(AdtId::UnionId(id)) => { | 116 | GenericDefId::AdtId(AdtId::UnionId(id)) => { |
117 | let id = id.lookup(db).id; | 117 | let id = id.lookup(db).id; |
118 | let tree = db.item_tree(id.file_id); | 118 | let tree = id.item_tree(db); |
119 | let item = &tree[id.value]; | 119 | let item = &tree[id.value]; |
120 | tree[item.generic_params].clone() | 120 | tree[item.generic_params].clone() |
121 | } | 121 | } |
122 | GenericDefId::TraitId(id) => { | 122 | GenericDefId::TraitId(id) => { |
123 | let id = id.lookup(db).id; | 123 | let id = id.lookup(db).id; |
124 | let tree = db.item_tree(id.file_id); | 124 | let tree = id.item_tree(db); |
125 | let item = &tree[id.value]; | 125 | let item = &tree[id.value]; |
126 | tree[item.generic_params].clone() | 126 | tree[item.generic_params].clone() |
127 | } | 127 | } |
128 | GenericDefId::TypeAliasId(id) => { | 128 | GenericDefId::TypeAliasId(id) => { |
129 | let id = id.lookup(db).id; | 129 | let id = id.lookup(db).id; |
130 | let tree = db.item_tree(id.file_id); | 130 | let tree = id.item_tree(db); |
131 | let item = &tree[id.value]; | 131 | let item = &tree[id.value]; |
132 | tree[item.generic_params].clone() | 132 | tree[item.generic_params].clone() |
133 | } | 133 | } |
134 | GenericDefId::ImplId(id) => { | 134 | GenericDefId::ImplId(id) => { |
135 | let id = id.lookup(db).id; | 135 | let id = id.lookup(db).id; |
136 | let tree = db.item_tree(id.file_id); | 136 | let tree = id.item_tree(db); |
137 | let item = &tree[id.value]; | 137 | let item = &tree[id.value]; |
138 | tree[item.generic_params].clone() | 138 | tree[item.generic_params].clone() |
139 | } | 139 | } |
diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs index 4477bdd36..ae2475b4e 100644 --- a/crates/hir_def/src/item_tree.rs +++ b/crates/hir_def/src/item_tree.rs | |||
@@ -76,7 +76,7 @@ pub struct ItemTree { | |||
76 | } | 76 | } |
77 | 77 | ||
78 | impl ItemTree { | 78 | impl ItemTree { |
79 | pub(crate) fn item_tree_query(db: &dyn DefDatabase, file_id: HirFileId) -> Arc<ItemTree> { | 79 | pub(crate) fn file_item_tree_query(db: &dyn DefDatabase, file_id: HirFileId) -> Arc<ItemTree> { |
80 | let _p = profile::span("item_tree_query").detail(|| format!("{:?}", file_id)); | 80 | let _p = profile::span("item_tree_query").detail(|| format!("{:?}", file_id)); |
81 | let syntax = if let Some(node) = db.parse_or_expand(file_id) { | 81 | let syntax = if let Some(node) = db.parse_or_expand(file_id) { |
82 | if node.kind() == SyntaxKind::ERROR { | 82 | if node.kind() == SyntaxKind::ERROR { |
@@ -138,6 +138,7 @@ impl ItemTree { | |||
138 | imports, | 138 | imports, |
139 | extern_crates, | 139 | extern_crates, |
140 | functions, | 140 | functions, |
141 | params, | ||
141 | structs, | 142 | structs, |
142 | fields, | 143 | fields, |
143 | unions, | 144 | unions, |
@@ -161,6 +162,7 @@ impl ItemTree { | |||
161 | imports.shrink_to_fit(); | 162 | imports.shrink_to_fit(); |
162 | extern_crates.shrink_to_fit(); | 163 | extern_crates.shrink_to_fit(); |
163 | functions.shrink_to_fit(); | 164 | functions.shrink_to_fit(); |
165 | params.shrink_to_fit(); | ||
164 | structs.shrink_to_fit(); | 166 | structs.shrink_to_fit(); |
165 | fields.shrink_to_fit(); | 167 | fields.shrink_to_fit(); |
166 | unions.shrink_to_fit(); | 168 | unions.shrink_to_fit(); |
@@ -307,6 +309,7 @@ struct ItemTreeData { | |||
307 | imports: Arena<Import>, | 309 | imports: Arena<Import>, |
308 | extern_crates: Arena<ExternCrate>, | 310 | extern_crates: Arena<ExternCrate>, |
309 | functions: Arena<Function>, | 311 | functions: Arena<Function>, |
312 | params: Arena<Param>, | ||
310 | structs: Arena<Struct>, | 313 | structs: Arena<Struct>, |
311 | fields: Arena<Field>, | 314 | fields: Arena<Field>, |
312 | unions: Arena<Union>, | 315 | unions: Arena<Union>, |
@@ -338,6 +341,7 @@ pub enum AttrOwner { | |||
338 | 341 | ||
339 | Variant(Idx<Variant>), | 342 | Variant(Idx<Variant>), |
340 | Field(Idx<Field>), | 343 | Field(Idx<Field>), |
344 | Param(Idx<Param>), | ||
341 | } | 345 | } |
342 | 346 | ||
343 | macro_rules! from_attrs { | 347 | macro_rules! from_attrs { |
@@ -352,7 +356,7 @@ macro_rules! from_attrs { | |||
352 | }; | 356 | }; |
353 | } | 357 | } |
354 | 358 | ||
355 | from_attrs!(ModItem(ModItem), Variant(Idx<Variant>), Field(Idx<Field>)); | 359 | from_attrs!(ModItem(ModItem), Variant(Idx<Variant>), Field(Idx<Field>), Param(Idx<Param>)); |
356 | 360 | ||
357 | /// Trait implemented by all item nodes in the item tree. | 361 | /// Trait implemented by all item nodes in the item tree. |
358 | pub trait ItemTreeNode: Clone { | 362 | pub trait ItemTreeNode: Clone { |
@@ -401,7 +405,47 @@ impl<N: ItemTreeNode> fmt::Debug for FileItemTreeId<N> { | |||
401 | } | 405 | } |
402 | } | 406 | } |
403 | 407 | ||
404 | pub type ItemTreeId<N> = InFile<FileItemTreeId<N>>; | 408 | #[derive(Debug)] |
409 | pub struct ItemTreeId<N: ItemTreeNode> { | ||
410 | file: HirFileId, | ||
411 | pub value: FileItemTreeId<N>, | ||
412 | } | ||
413 | |||
414 | impl<N: ItemTreeNode> ItemTreeId<N> { | ||
415 | pub fn new(file: HirFileId, idx: FileItemTreeId<N>) -> Self { | ||
416 | Self { file, value: idx } | ||
417 | } | ||
418 | |||
419 | pub fn file_id(self) -> HirFileId { | ||
420 | self.file | ||
421 | } | ||
422 | |||
423 | pub fn item_tree(self, db: &dyn DefDatabase) -> Arc<ItemTree> { | ||
424 | db.file_item_tree(self.file) | ||
425 | } | ||
426 | } | ||
427 | |||
428 | impl<N: ItemTreeNode> Copy for ItemTreeId<N> {} | ||
429 | impl<N: ItemTreeNode> Clone for ItemTreeId<N> { | ||
430 | fn clone(&self) -> Self { | ||
431 | *self | ||
432 | } | ||
433 | } | ||
434 | |||
435 | impl<N: ItemTreeNode> PartialEq for ItemTreeId<N> { | ||
436 | fn eq(&self, other: &Self) -> bool { | ||
437 | self.file == other.file && self.value == other.value | ||
438 | } | ||
439 | } | ||
440 | |||
441 | impl<N: ItemTreeNode> Eq for ItemTreeId<N> {} | ||
442 | |||
443 | impl<N: ItemTreeNode> Hash for ItemTreeId<N> { | ||
444 | fn hash<H: Hasher>(&self, state: &mut H) { | ||
445 | self.file.hash(state); | ||
446 | self.value.hash(state); | ||
447 | } | ||
448 | } | ||
405 | 449 | ||
406 | macro_rules! mod_items { | 450 | macro_rules! mod_items { |
407 | ( $( $typ:ident in $fld:ident -> $ast:ty ),+ $(,)? ) => { | 451 | ( $( $typ:ident in $fld:ident -> $ast:ty ),+ $(,)? ) => { |
@@ -488,7 +532,7 @@ macro_rules! impl_index { | |||
488 | }; | 532 | }; |
489 | } | 533 | } |
490 | 534 | ||
491 | impl_index!(fields: Field, variants: Variant); | 535 | impl_index!(fields: Field, variants: Variant, params: Param); |
492 | 536 | ||
493 | impl Index<RawVisibilityId> for ItemTree { | 537 | impl Index<RawVisibilityId> for ItemTree { |
494 | type Output = RawVisibility; | 538 | type Output = RawVisibility; |
@@ -564,12 +608,17 @@ pub struct Function { | |||
564 | /// Whether the function is located in an `extern` block (*not* whether it is an | 608 | /// Whether the function is located in an `extern` block (*not* whether it is an |
565 | /// `extern "abi" fn`). | 609 | /// `extern "abi" fn`). |
566 | pub is_in_extern_block: bool, | 610 | pub is_in_extern_block: bool, |
567 | pub params: Box<[Idx<TypeRef>]>, | 611 | pub params: IdRange<Param>, |
568 | pub is_varargs: bool, | ||
569 | pub ret_type: Idx<TypeRef>, | 612 | pub ret_type: Idx<TypeRef>, |
570 | pub ast_id: FileAstId<ast::Fn>, | 613 | pub ast_id: FileAstId<ast::Fn>, |
571 | } | 614 | } |
572 | 615 | ||
616 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
617 | pub enum Param { | ||
618 | Normal(Idx<TypeRef>), | ||
619 | Varargs, | ||
620 | } | ||
621 | |||
573 | #[derive(Debug, Clone, PartialEq, Eq)] | 622 | #[derive(Debug, Clone, PartialEq, Eq)] |
574 | pub struct FunctionQualifier { | 623 | pub struct FunctionQualifier { |
575 | pub is_default: bool, | 624 | pub is_default: bool, |
@@ -800,6 +849,7 @@ pub struct Variant { | |||
800 | pub fields: Fields, | 849 | pub fields: Fields, |
801 | } | 850 | } |
802 | 851 | ||
852 | /// A range of densely allocated ItemTree IDs. | ||
803 | pub struct IdRange<T> { | 853 | pub struct IdRange<T> { |
804 | range: Range<u32>, | 854 | range: Range<u32>, |
805 | _p: PhantomData<T>, | 855 | _p: PhantomData<T>, |
@@ -818,6 +868,12 @@ impl<T> Iterator for IdRange<T> { | |||
818 | } | 868 | } |
819 | } | 869 | } |
820 | 870 | ||
871 | impl<T> DoubleEndedIterator for IdRange<T> { | ||
872 | fn next_back(&mut self) -> Option<Self::Item> { | ||
873 | self.range.next_back().map(|raw| Idx::from_raw(raw.into())) | ||
874 | } | ||
875 | } | ||
876 | |||
821 | impl<T> fmt::Debug for IdRange<T> { | 877 | impl<T> fmt::Debug for IdRange<T> { |
822 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | 878 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
823 | f.debug_tuple(&format!("IdRange::<{}>", type_name::<T>())).field(&self.range).finish() | 879 | f.debug_tuple(&format!("IdRange::<{}>", type_name::<T>())).field(&self.range).finish() |
diff --git a/crates/hir_def/src/item_tree/lower.rs b/crates/hir_def/src/item_tree/lower.rs index d684b89d0..d3fe1ce1e 100644 --- a/crates/hir_def/src/item_tree/lower.rs +++ b/crates/hir_def/src/item_tree/lower.rs | |||
@@ -341,8 +341,8 @@ impl Ctx { | |||
341 | let visibility = self.lower_visibility(func); | 341 | let visibility = self.lower_visibility(func); |
342 | let name = func.name()?.as_name(); | 342 | let name = func.name()?.as_name(); |
343 | 343 | ||
344 | let mut params = Vec::new(); | ||
345 | let mut has_self_param = false; | 344 | let mut has_self_param = false; |
345 | let start_param = self.next_param_idx(); | ||
346 | if let Some(param_list) = func.param_list() { | 346 | if let Some(param_list) = func.param_list() { |
347 | if let Some(self_param) = param_list.self_param() { | 347 | if let Some(self_param) = param_list.self_param() { |
348 | let self_type = match self_param.ty() { | 348 | let self_type = match self_param.ty() { |
@@ -364,22 +364,25 @@ impl Ctx { | |||
364 | } | 364 | } |
365 | } | 365 | } |
366 | }; | 366 | }; |
367 | params.push(self_type); | 367 | let ty = self.data().type_refs.intern(self_type); |
368 | let idx = self.data().params.alloc(Param::Normal(ty)); | ||
369 | self.add_attrs(idx.into(), RawAttrs::new(&self_param, &self.hygiene)); | ||
368 | has_self_param = true; | 370 | has_self_param = true; |
369 | } | 371 | } |
370 | for param in param_list.params() { | 372 | for param in param_list.params() { |
371 | let type_ref = TypeRef::from_ast_opt(&self.body_ctx, param.ty()); | 373 | let idx = match param.dotdotdot_token() { |
372 | params.push(type_ref); | 374 | Some(_) => self.data().params.alloc(Param::Varargs), |
373 | } | 375 | None => { |
374 | } | 376 | let type_ref = TypeRef::from_ast_opt(&self.body_ctx, param.ty()); |
375 | let params = params.into_iter().map(|param| self.data().type_refs.intern(param)).collect(); | 377 | let ty = self.data().type_refs.intern(type_ref); |
376 | 378 | self.data().params.alloc(Param::Normal(ty)) | |
377 | let mut is_varargs = false; | 379 | } |
378 | if let Some(params) = func.param_list() { | 380 | }; |
379 | if let Some(last) = params.params().last() { | 381 | self.add_attrs(idx.into(), RawAttrs::new(¶m, &self.hygiene)); |
380 | is_varargs = last.dotdotdot_token().is_some(); | ||
381 | } | 382 | } |
382 | } | 383 | } |
384 | let end_param = self.next_param_idx(); | ||
385 | let params = IdRange::new(start_param..end_param); | ||
383 | 386 | ||
384 | let ret_type = match func.ret_type().and_then(|rt| rt.ty()) { | 387 | let ret_type = match func.ret_type().and_then(|rt| rt.ty()) { |
385 | Some(type_ref) => TypeRef::from_ast(&self.body_ctx, type_ref), | 388 | Some(type_ref) => TypeRef::from_ast(&self.body_ctx, type_ref), |
@@ -427,7 +430,6 @@ impl Ctx { | |||
427 | qualifier, | 430 | qualifier, |
428 | is_in_extern_block: false, | 431 | is_in_extern_block: false, |
429 | params, | 432 | params, |
430 | is_varargs, | ||
431 | ret_type, | 433 | ret_type, |
432 | ast_id, | 434 | ast_id, |
433 | }; | 435 | }; |
@@ -690,9 +692,11 @@ impl Ctx { | |||
690 | GenericsOwner::Function(func) => { | 692 | GenericsOwner::Function(func) => { |
691 | generics.fill(&self.body_ctx, sm, node); | 693 | generics.fill(&self.body_ctx, sm, node); |
692 | // lower `impl Trait` in arguments | 694 | // lower `impl Trait` in arguments |
693 | for param in &*func.params { | 695 | for id in func.params.clone() { |
694 | let param = self.data().type_refs.lookup(*param); | 696 | if let Param::Normal(ty) = self.data().params[id] { |
695 | generics.fill_implicit_impl_trait_args(param); | 697 | let ty = self.data().type_refs.lookup(ty); |
698 | generics.fill_implicit_impl_trait_args(ty); | ||
699 | } | ||
696 | } | 700 | } |
697 | } | 701 | } |
698 | GenericsOwner::Struct | 702 | GenericsOwner::Struct |
@@ -777,6 +781,11 @@ impl Ctx { | |||
777 | self.tree.data.as_ref().map_or(0, |data| data.variants.len() as u32), | 781 | self.tree.data.as_ref().map_or(0, |data| data.variants.len() as u32), |
778 | )) | 782 | )) |
779 | } | 783 | } |
784 | fn next_param_idx(&self) -> Idx<Param> { | ||
785 | Idx::from_raw(RawIdx::from( | ||
786 | self.tree.data.as_ref().map_or(0, |data| data.params.len() as u32), | ||
787 | )) | ||
788 | } | ||
780 | } | 789 | } |
781 | 790 | ||
782 | fn desugar_future_path(orig: TypeRef) -> Path { | 791 | fn desugar_future_path(orig: TypeRef) -> Path { |
diff --git a/crates/hir_def/src/nameres.rs b/crates/hir_def/src/nameres.rs index 003d668ca..c97be584e 100644 --- a/crates/hir_def/src/nameres.rs +++ b/crates/hir_def/src/nameres.rs | |||
@@ -213,7 +213,7 @@ impl DefMap { | |||
213 | ) -> Option<Arc<DefMap>> { | 213 | ) -> Option<Arc<DefMap>> { |
214 | let block: BlockLoc = db.lookup_intern_block(block_id); | 214 | let block: BlockLoc = db.lookup_intern_block(block_id); |
215 | 215 | ||
216 | let item_tree = db.item_tree(block.ast_id.file_id); | 216 | let item_tree = db.file_item_tree(block.ast_id.file_id); |
217 | if item_tree.inner_items_of_block(block.ast_id.value).is_empty() { | 217 | if item_tree.inner_items_of_block(block.ast_id.value).is_empty() { |
218 | return None; | 218 | return None; |
219 | } | 219 | } |
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs index 81cf652b0..d0fefb5af 100644 --- a/crates/hir_def/src/nameres/collector.rs +++ b/crates/hir_def/src/nameres/collector.rs | |||
@@ -242,7 +242,7 @@ struct DefCollector<'a> { | |||
242 | impl DefCollector<'_> { | 242 | impl DefCollector<'_> { |
243 | fn seed_with_top_level(&mut self) { | 243 | fn seed_with_top_level(&mut self) { |
244 | let file_id = self.db.crate_graph()[self.def_map.krate].root_file_id; | 244 | let file_id = self.db.crate_graph()[self.def_map.krate].root_file_id; |
245 | let item_tree = self.db.item_tree(file_id.into()); | 245 | let item_tree = self.db.file_item_tree(file_id.into()); |
246 | let module_id = self.def_map.root; | 246 | let module_id = self.def_map.root; |
247 | self.def_map.modules[module_id].origin = ModuleOrigin::CrateRoot { definition: file_id }; | 247 | self.def_map.modules[module_id].origin = ModuleOrigin::CrateRoot { definition: file_id }; |
248 | if item_tree | 248 | if item_tree |
@@ -263,7 +263,7 @@ impl DefCollector<'_> { | |||
263 | } | 263 | } |
264 | 264 | ||
265 | fn seed_with_inner(&mut self, block: AstId<ast::BlockExpr>) { | 265 | fn seed_with_inner(&mut self, block: AstId<ast::BlockExpr>) { |
266 | let item_tree = self.db.item_tree(block.file_id); | 266 | let item_tree = self.db.file_item_tree(block.file_id); |
267 | let module_id = self.def_map.root; | 267 | let module_id = self.def_map.root; |
268 | self.def_map.modules[module_id].origin = ModuleOrigin::BlockExpr { block }; | 268 | self.def_map.modules[module_id].origin = ModuleOrigin::BlockExpr { block }; |
269 | if item_tree | 269 | if item_tree |
@@ -656,26 +656,28 @@ impl DefCollector<'_> { | |||
656 | } | 656 | } |
657 | } | 657 | } |
658 | } else { | 658 | } else { |
659 | match import.path.segments().last() { | 659 | let name = match &import.alias { |
660 | Some(last_segment) => { | 660 | Some(ImportAlias::Alias(name)) => Some(name.clone()), |
661 | let name = match &import.alias { | 661 | Some(ImportAlias::Underscore) => None, |
662 | Some(ImportAlias::Alias(name)) => Some(name.clone()), | 662 | None => match import.path.segments().last() { |
663 | Some(ImportAlias::Underscore) => None, | 663 | Some(last_segment) => Some(last_segment.clone()), |
664 | None => Some(last_segment.clone()), | 664 | None => { |
665 | }; | 665 | cov_mark::hit!(bogus_paths); |
666 | log::debug!("resolved import {:?} ({:?}) to {:?}", name, import, def); | 666 | return; |
667 | |||
668 | // extern crates in the crate root are special-cased to insert entries into the extern prelude: rust-lang/rust#54658 | ||
669 | if import.is_extern_crate && module_id == self.def_map.root { | ||
670 | if let (Some(def), Some(name)) = (def.take_types(), name.as_ref()) { | ||
671 | self.def_map.extern_prelude.insert(name.clone(), def); | ||
672 | } | ||
673 | } | 667 | } |
668 | }, | ||
669 | }; | ||
670 | |||
671 | log::debug!("resolved import {:?} ({:?}) to {:?}", name, import, def); | ||
674 | 672 | ||
675 | self.update(module_id, &[(name, def)], vis, ImportType::Named); | 673 | // extern crates in the crate root are special-cased to insert entries into the extern prelude: rust-lang/rust#54658 |
674 | if import.is_extern_crate && module_id == self.def_map.root { | ||
675 | if let (Some(def), Some(name)) = (def.take_types(), name.as_ref()) { | ||
676 | self.def_map.extern_prelude.insert(name.clone(), def); | ||
676 | } | 677 | } |
677 | None => cov_mark::hit!(bogus_paths), | ||
678 | } | 678 | } |
679 | |||
680 | self.update(module_id, &[(name, def)], vis, ImportType::Named); | ||
679 | } | 681 | } |
680 | } | 682 | } |
681 | 683 | ||
@@ -893,7 +895,7 @@ impl DefCollector<'_> { | |||
893 | } | 895 | } |
894 | 896 | ||
895 | // Then, fetch and process the item tree. This will reuse the expansion result from above. | 897 | // Then, fetch and process the item tree. This will reuse the expansion result from above. |
896 | let item_tree = self.db.item_tree(file_id); | 898 | let item_tree = self.db.file_item_tree(file_id); |
897 | let mod_dir = self.mod_dirs[&module_id].clone(); | 899 | let mod_dir = self.mod_dirs[&module_id].clone(); |
898 | ModCollector { | 900 | ModCollector { |
899 | def_collector: &mut *self, | 901 | def_collector: &mut *self, |
@@ -949,21 +951,21 @@ impl DefCollector<'_> { | |||
949 | let mut diagnosed_extern_crates = FxHashSet::default(); | 951 | let mut diagnosed_extern_crates = FxHashSet::default(); |
950 | for directive in &self.unresolved_imports { | 952 | for directive in &self.unresolved_imports { |
951 | if let ImportSource::ExternCrate(krate) = directive.import.source { | 953 | if let ImportSource::ExternCrate(krate) = directive.import.source { |
952 | let item_tree = self.db.item_tree(krate.file_id); | 954 | let item_tree = krate.item_tree(self.db); |
953 | let extern_crate = &item_tree[krate.value]; | 955 | let extern_crate = &item_tree[krate.value]; |
954 | 956 | ||
955 | diagnosed_extern_crates.insert(extern_crate.name.clone()); | 957 | diagnosed_extern_crates.insert(extern_crate.name.clone()); |
956 | 958 | ||
957 | self.def_map.diagnostics.push(DefDiagnostic::unresolved_extern_crate( | 959 | self.def_map.diagnostics.push(DefDiagnostic::unresolved_extern_crate( |
958 | directive.module_id, | 960 | directive.module_id, |
959 | InFile::new(krate.file_id, extern_crate.ast_id), | 961 | InFile::new(krate.file_id(), extern_crate.ast_id), |
960 | )); | 962 | )); |
961 | } | 963 | } |
962 | } | 964 | } |
963 | 965 | ||
964 | for directive in &self.unresolved_imports { | 966 | for directive in &self.unresolved_imports { |
965 | if let ImportSource::Import(import) = &directive.import.source { | 967 | if let ImportSource::Import(import) = &directive.import.source { |
966 | let item_tree = self.db.item_tree(import.file_id); | 968 | let item_tree = import.item_tree(self.db); |
967 | let import_data = &item_tree[import.value]; | 969 | let import_data = &item_tree[import.value]; |
968 | 970 | ||
969 | match (import_data.path.segments().first(), &import_data.path.kind) { | 971 | match (import_data.path.segments().first(), &import_data.path.kind) { |
@@ -977,7 +979,7 @@ impl DefCollector<'_> { | |||
977 | 979 | ||
978 | self.def_map.diagnostics.push(DefDiagnostic::unresolved_import( | 980 | self.def_map.diagnostics.push(DefDiagnostic::unresolved_import( |
979 | directive.module_id, | 981 | directive.module_id, |
980 | InFile::new(import.file_id, import_data.ast_id), | 982 | InFile::new(import.file_id(), import_data.ast_id), |
981 | import_data.index, | 983 | import_data.index, |
982 | )); | 984 | )); |
983 | } | 985 | } |
@@ -1053,7 +1055,7 @@ impl ModCollector<'_, '_> { | |||
1053 | self.def_collector.db, | 1055 | self.def_collector.db, |
1054 | krate, | 1056 | krate, |
1055 | &self.item_tree, | 1057 | &self.item_tree, |
1056 | InFile::new(self.file_id, import_id), | 1058 | ItemTreeId::new(self.file_id, import_id), |
1057 | ), | 1059 | ), |
1058 | status: PartialResolvedImport::Unresolved, | 1060 | status: PartialResolvedImport::Unresolved, |
1059 | }) | 1061 | }) |
@@ -1065,7 +1067,7 @@ impl ModCollector<'_, '_> { | |||
1065 | self.def_collector.db, | 1067 | self.def_collector.db, |
1066 | krate, | 1068 | krate, |
1067 | &self.item_tree, | 1069 | &self.item_tree, |
1068 | InFile::new(self.file_id, import_id), | 1070 | ItemTreeId::new(self.file_id, import_id), |
1069 | ), | 1071 | ), |
1070 | status: PartialResolvedImport::Unresolved, | 1072 | status: PartialResolvedImport::Unresolved, |
1071 | }) | 1073 | }) |
@@ -1297,7 +1299,7 @@ impl ModCollector<'_, '_> { | |||
1297 | Some((file_id, is_mod_rs)), | 1299 | Some((file_id, is_mod_rs)), |
1298 | &self.item_tree[module.visibility], | 1300 | &self.item_tree[module.visibility], |
1299 | ); | 1301 | ); |
1300 | let item_tree = db.item_tree(file_id.into()); | 1302 | let item_tree = db.file_item_tree(file_id.into()); |
1301 | ModCollector { | 1303 | ModCollector { |
1302 | def_collector: &mut *self.def_collector, | 1304 | def_collector: &mut *self.def_collector, |
1303 | macro_depth: self.macro_depth, | 1305 | macro_depth: self.macro_depth, |
@@ -1469,7 +1471,9 @@ impl ModCollector<'_, '_> { | |||
1469 | ) | 1471 | ) |
1470 | }) | 1472 | }) |
1471 | }, | 1473 | }, |
1472 | &mut |err| error = Some(err), | 1474 | &mut |err| { |
1475 | error.get_or_insert(err); | ||
1476 | }, | ||
1473 | ) { | 1477 | ) { |
1474 | Ok(Ok(macro_call_id)) => { | 1478 | Ok(Ok(macro_call_id)) => { |
1475 | self.def_collector.unexpanded_macros.push(MacroDirective { | 1479 | self.def_collector.unexpanded_macros.push(MacroDirective { |
diff --git a/crates/hir_def/src/nameres/tests.rs b/crates/hir_def/src/nameres/tests.rs index de3aa4f9a..4f2e7a2f9 100644 --- a/crates/hir_def/src/nameres/tests.rs +++ b/crates/hir_def/src/nameres/tests.rs | |||
@@ -713,3 +713,22 @@ pub fn f() {} | |||
713 | "#]], | 713 | "#]], |
714 | ); | 714 | ); |
715 | } | 715 | } |
716 | |||
717 | #[test] | ||
718 | fn use_crate_as() { | ||
719 | check( | ||
720 | r#" | ||
721 | use crate as foo; | ||
722 | |||
723 | use foo::bar as baz; | ||
724 | |||
725 | fn bar() {} | ||
726 | "#, | ||
727 | expect![[r#" | ||
728 | crate | ||
729 | bar: v | ||
730 | baz: v | ||
731 | foo: t | ||
732 | "#]], | ||
733 | ); | ||
734 | } | ||
diff --git a/crates/hir_def/src/nameres/tests/diagnostics.rs b/crates/hir_def/src/nameres/tests/diagnostics.rs index c22ef46fd..a89061c2e 100644 --- a/crates/hir_def/src/nameres/tests/diagnostics.rs +++ b/crates/hir_def/src/nameres/tests/diagnostics.rs | |||
@@ -196,7 +196,24 @@ fn builtin_macro_fails_expansion() { | |||
196 | macro_rules! include { () => {} } | 196 | macro_rules! include { () => {} } |
197 | 197 | ||
198 | include!("doesntexist"); | 198 | include!("doesntexist"); |
199 | //^^^^^^^^^^^^^^^^^^^^^^^^ could not convert tokens | 199 | //^^^^^^^^^^^^^^^^^^^^^^^^ failed to load file `doesntexist` |
200 | "#, | ||
201 | ); | ||
202 | } | ||
203 | |||
204 | #[test] | ||
205 | fn good_out_dir_diagnostic() { | ||
206 | check_diagnostics( | ||
207 | r#" | ||
208 | #[rustc_builtin_macro] | ||
209 | macro_rules! include { () => {} } | ||
210 | #[rustc_builtin_macro] | ||
211 | macro_rules! env { () => {} } | ||
212 | #[rustc_builtin_macro] | ||
213 | macro_rules! concat { () => {} } | ||
214 | |||
215 | include!(concat!(env!("OUT_DIR"), "/out.rs")); | ||
216 | //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `OUT_DIR` not set, enable "load out dirs from check" to fix | ||
200 | "#, | 217 | "#, |
201 | ); | 218 | ); |
202 | } | 219 | } |
diff --git a/crates/hir_def/src/src.rs b/crates/hir_def/src/src.rs index 751d4c052..24e57b469 100644 --- a/crates/hir_def/src/src.rs +++ b/crates/hir_def/src/src.rs | |||
@@ -14,12 +14,12 @@ impl<N: ItemTreeNode> HasSource for AssocItemLoc<N> { | |||
14 | type Value = N::Source; | 14 | type Value = N::Source; |
15 | 15 | ||
16 | fn source(&self, db: &dyn DefDatabase) -> InFile<N::Source> { | 16 | fn source(&self, db: &dyn DefDatabase) -> InFile<N::Source> { |
17 | let tree = db.item_tree(self.id.file_id); | 17 | let tree = self.id.item_tree(db); |
18 | let ast_id_map = db.ast_id_map(self.id.file_id); | 18 | let ast_id_map = db.ast_id_map(self.id.file_id()); |
19 | let root = db.parse_or_expand(self.id.file_id).unwrap(); | 19 | let root = db.parse_or_expand(self.id.file_id()).unwrap(); |
20 | let node = &tree[self.id.value]; | 20 | let node = &tree[self.id.value]; |
21 | 21 | ||
22 | InFile::new(self.id.file_id, ast_id_map.get(node.ast_id()).to_node(&root)) | 22 | InFile::new(self.id.file_id(), ast_id_map.get(node.ast_id()).to_node(&root)) |
23 | } | 23 | } |
24 | } | 24 | } |
25 | 25 | ||
@@ -27,12 +27,12 @@ impl<N: ItemTreeNode> HasSource for ItemLoc<N> { | |||
27 | type Value = N::Source; | 27 | type Value = N::Source; |
28 | 28 | ||
29 | fn source(&self, db: &dyn DefDatabase) -> InFile<N::Source> { | 29 | fn source(&self, db: &dyn DefDatabase) -> InFile<N::Source> { |
30 | let tree = db.item_tree(self.id.file_id); | 30 | let tree = self.id.item_tree(db); |
31 | let ast_id_map = db.ast_id_map(self.id.file_id); | 31 | let ast_id_map = db.ast_id_map(self.id.file_id()); |
32 | let root = db.parse_or_expand(self.id.file_id).unwrap(); | 32 | let root = db.parse_or_expand(self.id.file_id()).unwrap(); |
33 | let node = &tree[self.id.value]; | 33 | let node = &tree[self.id.value]; |
34 | 34 | ||
35 | InFile::new(self.id.file_id, ast_id_map.get(node.ast_id()).to_node(&root)) | 35 | InFile::new(self.id.file_id(), ast_id_map.get(node.ast_id()).to_node(&root)) |
36 | } | 36 | } |
37 | } | 37 | } |
38 | 38 | ||
diff --git a/crates/hir_expand/src/builtin_macro.rs b/crates/hir_expand/src/builtin_macro.rs index 2a79c892b..fce09a9e7 100644 --- a/crates/hir_expand/src/builtin_macro.rs +++ b/crates/hir_expand/src/builtin_macro.rs | |||
@@ -333,17 +333,19 @@ fn concat_expand( | |||
333 | fn relative_file( | 333 | fn relative_file( |
334 | db: &dyn AstDatabase, | 334 | db: &dyn AstDatabase, |
335 | call_id: MacroCallId, | 335 | call_id: MacroCallId, |
336 | path: &str, | 336 | path_str: &str, |
337 | allow_recursion: bool, | 337 | allow_recursion: bool, |
338 | ) -> Option<FileId> { | 338 | ) -> Result<FileId, mbe::ExpandError> { |
339 | let call_site = call_id.as_file().original_file(db); | 339 | let call_site = call_id.as_file().original_file(db); |
340 | let path = AnchoredPath { anchor: call_site, path }; | 340 | let path = AnchoredPath { anchor: call_site, path: path_str }; |
341 | let res = db.resolve_path(path)?; | 341 | let res = db |
342 | .resolve_path(path) | ||
343 | .ok_or_else(|| mbe::ExpandError::Other(format!("failed to load file `{}`", path_str)))?; | ||
342 | // Prevent include itself | 344 | // Prevent include itself |
343 | if res == call_site && !allow_recursion { | 345 | if res == call_site && !allow_recursion { |
344 | None | 346 | Err(mbe::ExpandError::Other(format!("recursive inclusion of `{}`", path_str))) |
345 | } else { | 347 | } else { |
346 | Some(res) | 348 | Ok(res) |
347 | } | 349 | } |
348 | } | 350 | } |
349 | 351 | ||
@@ -364,8 +366,7 @@ fn include_expand( | |||
364 | ) -> ExpandResult<Option<(tt::Subtree, FragmentKind)>> { | 366 | ) -> ExpandResult<Option<(tt::Subtree, FragmentKind)>> { |
365 | let res = (|| { | 367 | let res = (|| { |
366 | let path = parse_string(tt)?; | 368 | let path = parse_string(tt)?; |
367 | let file_id = relative_file(db, arg_id.into(), &path, false) | 369 | let file_id = relative_file(db, arg_id.into(), &path, false)?; |
368 | .ok_or_else(|| mbe::ExpandError::ConversionError)?; | ||
369 | 370 | ||
370 | Ok(parse_to_token_tree(&db.file_text(file_id)) | 371 | Ok(parse_to_token_tree(&db.file_text(file_id)) |
371 | .ok_or_else(|| mbe::ExpandError::ConversionError)? | 372 | .ok_or_else(|| mbe::ExpandError::ConversionError)? |
@@ -417,8 +418,8 @@ fn include_str_expand( | |||
417 | // Ideally, we'd be able to offer a precise expansion if the user asks for macro | 418 | // Ideally, we'd be able to offer a precise expansion if the user asks for macro |
418 | // expansion. | 419 | // expansion. |
419 | let file_id = match relative_file(db, arg_id.into(), &path, true) { | 420 | let file_id = match relative_file(db, arg_id.into(), &path, true) { |
420 | Some(file_id) => file_id, | 421 | Ok(file_id) => file_id, |
421 | None => { | 422 | Err(_) => { |
422 | return ExpandResult::ok(Some((quote!(""), FragmentKind::Expr))); | 423 | return ExpandResult::ok(Some((quote!(""), FragmentKind::Expr))); |
423 | } | 424 | } |
424 | }; | 425 | }; |
diff --git a/crates/hir_ty/src/diagnostics/expr.rs b/crates/hir_ty/src/diagnostics/expr.rs index 71b2cade0..3909ad354 100644 --- a/crates/hir_ty/src/diagnostics/expr.rs +++ b/crates/hir_ty/src/diagnostics/expr.rs | |||
@@ -690,4 +690,61 @@ fn main() { | |||
690 | "#, | 690 | "#, |
691 | ) | 691 | ) |
692 | } | 692 | } |
693 | |||
694 | #[test] | ||
695 | fn cfgd_out_call_arguments() { | ||
696 | check_diagnostics( | ||
697 | r#" | ||
698 | struct C(#[cfg(FALSE)] ()); | ||
699 | impl C { | ||
700 | fn new() -> Self { | ||
701 | Self( | ||
702 | #[cfg(FALSE)] | ||
703 | (), | ||
704 | ) | ||
705 | } | ||
706 | |||
707 | fn method(&self) {} | ||
708 | } | ||
709 | |||
710 | fn main() { | ||
711 | C::new().method(#[cfg(FALSE)] 0); | ||
712 | } | ||
713 | "#, | ||
714 | ); | ||
715 | } | ||
716 | |||
717 | #[test] | ||
718 | fn cfgd_out_fn_params() { | ||
719 | check_diagnostics( | ||
720 | r#" | ||
721 | fn foo(#[cfg(NEVER)] x: ()) {} | ||
722 | |||
723 | struct S; | ||
724 | |||
725 | impl S { | ||
726 | fn method(#[cfg(NEVER)] self) {} | ||
727 | fn method2(#[cfg(NEVER)] self, arg: u8) {} | ||
728 | fn method3(self, #[cfg(NEVER)] arg: u8) {} | ||
729 | } | ||
730 | |||
731 | extern "C" { | ||
732 | fn fixed(fixed: u8, #[cfg(NEVER)] ...); | ||
733 | fn varargs(#[cfg(not(NEVER))] ...); | ||
734 | } | ||
735 | |||
736 | fn main() { | ||
737 | foo(); | ||
738 | S::method(); | ||
739 | S::method2(0); | ||
740 | S::method3(S); | ||
741 | S.method3(); | ||
742 | unsafe { | ||
743 | fixed(0); | ||
744 | varargs(1, 2, 3); | ||
745 | } | ||
746 | } | ||
747 | "#, | ||
748 | ) | ||
749 | } | ||
693 | } | 750 | } |
diff --git a/crates/hir_ty/src/tests/simple.rs b/crates/hir_ty/src/tests/simple.rs index f5069eba5..bcc43ed70 100644 --- a/crates/hir_ty/src/tests/simple.rs +++ b/crates/hir_ty/src/tests/simple.rs | |||
@@ -2545,3 +2545,22 @@ fn test() { | |||
2545 | "#]], | 2545 | "#]], |
2546 | ) | 2546 | ) |
2547 | } | 2547 | } |
2548 | |||
2549 | #[test] | ||
2550 | fn cfgd_out_assoc_items() { | ||
2551 | check_types( | ||
2552 | r#" | ||
2553 | struct S; | ||
2554 | |||
2555 | impl S { | ||
2556 | #[cfg(FALSE)] | ||
2557 | const C: S = S; | ||
2558 | } | ||
2559 | |||
2560 | fn f() { | ||
2561 | S::C; | ||
2562 | //^^^^ {unknown} | ||
2563 | } | ||
2564 | "#, | ||
2565 | ) | ||
2566 | } | ||
diff --git a/crates/hir_ty/src/tests/traits.rs b/crates/hir_ty/src/tests/traits.rs index 93d3ad020..8270fa219 100644 --- a/crates/hir_ty/src/tests/traits.rs +++ b/crates/hir_ty/src/tests/traits.rs | |||
@@ -3253,3 +3253,24 @@ fn f() { | |||
3253 | "#, | 3253 | "#, |
3254 | ); | 3254 | ); |
3255 | } | 3255 | } |
3256 | |||
3257 | #[test] | ||
3258 | fn nested_inner_function_calling_self() { | ||
3259 | check_infer( | ||
3260 | r#" | ||
3261 | struct S; | ||
3262 | fn f() { | ||
3263 | fn inner() -> S { | ||
3264 | let s = inner(); | ||
3265 | } | ||
3266 | } | ||
3267 | "#, | ||
3268 | expect![[r#" | ||
3269 | 17..73 '{ ... } }': () | ||
3270 | 39..71 '{ ... }': () | ||
3271 | 53..54 's': S | ||
3272 | 57..62 'inner': fn inner() -> S | ||
3273 | 57..64 'inner()': S | ||
3274 | "#]], | ||
3275 | ) | ||
3276 | } | ||
diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs index 5ea9fc4fb..c7c1f4fee 100644 --- a/crates/ide/src/doc_links.rs +++ b/crates/ide/src/doc_links.rs | |||
@@ -65,6 +65,8 @@ pub(crate) fn extract_definitions_from_markdown( | |||
65 | ) -> Vec<(String, Option<hir::Namespace>, Range<usize>)> { | 65 | ) -> Vec<(String, Option<hir::Namespace>, Range<usize>)> { |
66 | let mut res = vec![]; | 66 | let mut res = vec![]; |
67 | let mut cb = |link: BrokenLink| { | 67 | let mut cb = |link: BrokenLink| { |
68 | // These allocations are actually unnecessary but the lifetimes on BrokenLinkCallback are wrong | ||
69 | // this is fixed in the repo but not on the crates.io release yet | ||
68 | Some(( | 70 | Some(( |
69 | /*url*/ link.reference.to_owned().into(), | 71 | /*url*/ link.reference.to_owned().into(), |
70 | /*title*/ link.reference.to_owned().into(), | 72 | /*title*/ link.reference.to_owned().into(), |
@@ -72,13 +74,10 @@ pub(crate) fn extract_definitions_from_markdown( | |||
72 | }; | 74 | }; |
73 | let doc = Parser::new_with_broken_link_callback(markdown, Options::empty(), Some(&mut cb)); | 75 | let doc = Parser::new_with_broken_link_callback(markdown, Options::empty(), Some(&mut cb)); |
74 | for (event, range) in doc.into_offset_iter() { | 76 | for (event, range) in doc.into_offset_iter() { |
75 | match event { | 77 | if let Event::Start(Tag::Link(_, target, title)) = event { |
76 | Event::Start(Tag::Link(_link_type, ref target, ref title)) => { | 78 | let link = if target.is_empty() { title } else { target }; |
77 | let link = if target.is_empty() { title } else { target }; | 79 | let (link, ns) = parse_link(&link); |
78 | let (link, ns) = parse_link(link); | 80 | res.push((link.to_string(), ns, range)); |
79 | res.push((link.to_string(), ns, range)); | ||
80 | } | ||
81 | _ => {} | ||
82 | } | 81 | } |
83 | } | 82 | } |
84 | res | 83 | res |
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs index b71f4917c..598b47e41 100644 --- a/crates/ide/src/goto_definition.rs +++ b/crates/ide/src/goto_definition.rs | |||
@@ -1,3 +1,5 @@ | |||
1 | use std::ops::Range; | ||
2 | |||
1 | use either::Either; | 3 | use either::Either; |
2 | use hir::{HasAttrs, ModuleDef, Semantics}; | 4 | use hir::{HasAttrs, ModuleDef, Semantics}; |
3 | use ide_db::{ | 5 | use ide_db::{ |
@@ -5,7 +7,8 @@ use ide_db::{ | |||
5 | RootDatabase, | 7 | RootDatabase, |
6 | }; | 8 | }; |
7 | use syntax::{ | 9 | use syntax::{ |
8 | ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, TextSize, TokenAtOffset, T, | 10 | ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, TextRange, TextSize, |
11 | TokenAtOffset, T, | ||
9 | }; | 12 | }; |
10 | 13 | ||
11 | use crate::{ | 14 | use crate::{ |
@@ -92,17 +95,18 @@ fn extract_positioned_link_from_comment( | |||
92 | position: FilePosition, | 95 | position: FilePosition, |
93 | comment: &ast::Comment, | 96 | comment: &ast::Comment, |
94 | ) -> Option<(String, Option<hir::Namespace>)> { | 97 | ) -> Option<(String, Option<hir::Namespace>)> { |
95 | let comment_range = comment.syntax().text_range(); | ||
96 | let doc_comment = comment.doc_comment()?; | 98 | let doc_comment = comment.doc_comment()?; |
99 | let comment_start = | ||
100 | comment.syntax().text_range().start() + TextSize::from(comment.prefix().len() as u32); | ||
97 | let def_links = extract_definitions_from_markdown(doc_comment); | 101 | let def_links = extract_definitions_from_markdown(doc_comment); |
98 | let (def_link, ns, _) = def_links.iter().min_by_key(|(_, _, def_link_range)| { | 102 | let (def_link, ns, _) = def_links.into_iter().find(|&(_, _, Range { start, end })| { |
99 | let matched_position = comment_range.start() + TextSize::from(def_link_range.start as u32); | 103 | TextRange::at( |
100 | match position.offset.checked_sub(matched_position) { | 104 | comment_start + TextSize::from(start as u32), |
101 | Some(distance) => distance, | 105 | TextSize::from((end - start) as u32), |
102 | None => comment_range.end(), | 106 | ) |
103 | } | 107 | .contains(position.offset) |
104 | })?; | 108 | })?; |
105 | Some((def_link.to_string(), *ns)) | 109 | Some((def_link, ns)) |
106 | } | 110 | } |
107 | 111 | ||
108 | fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> { | 112 | fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> { |
@@ -1136,7 +1140,7 @@ fn foo<'foo>(_: &'foo ()) { | |||
1136 | fn goto_def_for_intra_doc_link_same_file() { | 1140 | fn goto_def_for_intra_doc_link_same_file() { |
1137 | check( | 1141 | check( |
1138 | r#" | 1142 | r#" |
1139 | /// Blah, [`bar`](bar) .. [`foo`](foo)$0 has [`bar`](bar) | 1143 | /// Blah, [`bar`](bar) .. [`foo`](foo$0) has [`bar`](bar) |
1140 | pub fn bar() { } | 1144 | pub fn bar() { } |
1141 | 1145 | ||
1142 | /// You might want to see [`std::fs::read()`] too. | 1146 | /// You might want to see [`std::fs::read()`] too. |
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index 325014622..15d309d7d 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs | |||
@@ -1533,12 +1533,21 @@ fn my() {} | |||
1533 | fn test_hover_struct_doc_comment() { | 1533 | fn test_hover_struct_doc_comment() { |
1534 | check( | 1534 | check( |
1535 | r#" | 1535 | r#" |
1536 | /// bar docs | 1536 | /// This is an example |
1537 | /// multiline doc | ||
1538 | /// | ||
1539 | /// # Example | ||
1540 | /// | ||
1541 | /// ``` | ||
1542 | /// let five = 5; | ||
1543 | /// | ||
1544 | /// assert_eq!(6, my_crate::add_one(5)); | ||
1545 | /// ``` | ||
1537 | struct Bar; | 1546 | struct Bar; |
1538 | 1547 | ||
1539 | fn foo() { let bar = Ba$0r; } | 1548 | fn foo() { let bar = Ba$0r; } |
1540 | "#, | 1549 | "#, |
1541 | expect![[r#" | 1550 | expect![[r##" |
1542 | *Bar* | 1551 | *Bar* |
1543 | 1552 | ||
1544 | ```rust | 1553 | ```rust |
@@ -1551,8 +1560,17 @@ fn foo() { let bar = Ba$0r; } | |||
1551 | 1560 | ||
1552 | --- | 1561 | --- |
1553 | 1562 | ||
1554 | bar docs | 1563 | This is an example |
1555 | "#]], | 1564 | multiline doc |
1565 | |||
1566 | # Example | ||
1567 | |||
1568 | ``` | ||
1569 | let five = 5; | ||
1570 | |||
1571 | assert_eq!(6, my_crate::add_one(5)); | ||
1572 | ``` | ||
1573 | "##]], | ||
1556 | ); | 1574 | ); |
1557 | } | 1575 | } |
1558 | 1576 | ||
@@ -3424,6 +3442,40 @@ mod Foo$0 { | |||
3424 | } | 3442 | } |
3425 | 3443 | ||
3426 | #[test] | 3444 | #[test] |
3445 | fn hover_doc_block_style_indentend() { | ||
3446 | check( | ||
3447 | r#" | ||
3448 | /** | ||
3449 | foo | ||
3450 | ```rust | ||
3451 | let x = 3; | ||
3452 | ``` | ||
3453 | */ | ||
3454 | fn foo$0() {} | ||
3455 | "#, | ||
3456 | expect![[r#" | ||
3457 | *foo* | ||
3458 | |||
3459 | ```rust | ||
3460 | test | ||
3461 | ``` | ||
3462 | |||
3463 | ```rust | ||
3464 | fn foo() | ||
3465 | ``` | ||
3466 | |||
3467 | --- | ||
3468 | |||
3469 | foo | ||
3470 | |||
3471 | ```rust | ||
3472 | let x = 3; | ||
3473 | ``` | ||
3474 | "#]], | ||
3475 | ); | ||
3476 | } | ||
3477 | |||
3478 | #[test] | ||
3427 | fn hover_comments_dont_highlight_parent() { | 3479 | fn hover_comments_dont_highlight_parent() { |
3428 | check_hover_no_result( | 3480 | check_hover_no_result( |
3429 | r#" | 3481 | r#" |
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs index 397e2126b..bea020b06 100644 --- a/crates/ide/src/runnables.rs +++ b/crates/ide/src/runnables.rs | |||
@@ -576,6 +576,20 @@ fn should_have_runnable_1() {} | |||
576 | /// ``` | 576 | /// ``` |
577 | fn should_have_runnable_2() {} | 577 | fn should_have_runnable_2() {} |
578 | 578 | ||
579 | /** | ||
580 | ```rust | ||
581 | let z = 55; | ||
582 | ``` | ||
583 | */ | ||
584 | fn should_have_no_runnable_3() {} | ||
585 | |||
586 | /** | ||
587 | ```rust | ||
588 | let z = 55; | ||
589 | ``` | ||
590 | */ | ||
591 | fn should_have_no_runnable_4() {} | ||
592 | |||
579 | /// ```no_run | 593 | /// ```no_run |
580 | /// let z = 55; | 594 | /// let z = 55; |
581 | /// ``` | 595 | /// ``` |
@@ -616,7 +630,7 @@ fn should_have_no_runnable_6() {} | |||
616 | struct StructWithRunnable(String); | 630 | struct StructWithRunnable(String); |
617 | 631 | ||
618 | "#, | 632 | "#, |
619 | &[&BIN, &DOCTEST, &DOCTEST, &DOCTEST, &DOCTEST], | 633 | &[&BIN, &DOCTEST, &DOCTEST, &DOCTEST, &DOCTEST, &DOCTEST, &DOCTEST], |
620 | expect![[r#" | 634 | expect![[r#" |
621 | [ | 635 | [ |
622 | Runnable { | 636 | Runnable { |
@@ -682,7 +696,37 @@ struct StructWithRunnable(String); | |||
682 | file_id: FileId( | 696 | file_id: FileId( |
683 | 0, | 697 | 0, |
684 | ), | 698 | ), |
685 | full_range: 756..821, | 699 | full_range: 256..320, |
700 | name: "should_have_no_runnable_3", | ||
701 | }, | ||
702 | kind: DocTest { | ||
703 | test_id: Path( | ||
704 | "should_have_no_runnable_3", | ||
705 | ), | ||
706 | }, | ||
707 | cfg: None, | ||
708 | }, | ||
709 | Runnable { | ||
710 | nav: NavigationTarget { | ||
711 | file_id: FileId( | ||
712 | 0, | ||
713 | ), | ||
714 | full_range: 322..398, | ||
715 | name: "should_have_no_runnable_4", | ||
716 | }, | ||
717 | kind: DocTest { | ||
718 | test_id: Path( | ||
719 | "should_have_no_runnable_4", | ||
720 | ), | ||
721 | }, | ||
722 | cfg: None, | ||
723 | }, | ||
724 | Runnable { | ||
725 | nav: NavigationTarget { | ||
726 | file_id: FileId( | ||
727 | 0, | ||
728 | ), | ||
729 | full_range: 900..965, | ||
686 | name: "StructWithRunnable", | 730 | name: "StructWithRunnable", |
687 | }, | 731 | }, |
688 | kind: DocTest { | 732 | kind: DocTest { |
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs index 870146d24..ba3447b3a 100644 --- a/crates/ide/src/syntax_highlighting.rs +++ b/crates/ide/src/syntax_highlighting.rs | |||
@@ -150,7 +150,7 @@ fn traverse( | |||
150 | WalkEvent::Enter(it) => it, | 150 | WalkEvent::Enter(it) => it, |
151 | WalkEvent::Leave(it) => { | 151 | WalkEvent::Leave(it) => { |
152 | if let Some(node) = it.as_node() { | 152 | if let Some(node) = it.as_node() { |
153 | inject::doc_comment(hl, node); | 153 | inject::doc_comment(hl, sema, node); |
154 | } | 154 | } |
155 | continue; | 155 | continue; |
156 | } | 156 | } |
diff --git a/crates/ide/src/syntax_highlighting/html.rs b/crates/ide/src/syntax_highlighting/html.rs index 0ee7bc96e..1d34731ab 100644 --- a/crates/ide/src/syntax_highlighting/html.rs +++ b/crates/ide/src/syntax_highlighting/html.rs | |||
@@ -59,6 +59,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
59 | .label { color: #DFAF8F; font-style: italic; } | 59 | .label { color: #DFAF8F; font-style: italic; } |
60 | .comment { color: #7F9F7F; } | 60 | .comment { color: #7F9F7F; } |
61 | .documentation { color: #629755; } | 61 | .documentation { color: #629755; } |
62 | .intra_doc_link { color: #A9C577; } | ||
62 | .injected { opacity: 0.65 ; } | 63 | .injected { opacity: 0.65 ; } |
63 | .struct, .enum { color: #7CB8BB; } | 64 | .struct, .enum { color: #7CB8BB; } |
64 | .enum_variant { color: #BDE0F3; } | 65 | .enum_variant { color: #BDE0F3; } |
diff --git a/crates/ide/src/syntax_highlighting/inject.rs b/crates/ide/src/syntax_highlighting/inject.rs index 4f825523c..947cc974c 100644 --- a/crates/ide/src/syntax_highlighting/inject.rs +++ b/crates/ide/src/syntax_highlighting/inject.rs | |||
@@ -1,10 +1,18 @@ | |||
1 | //! "Recursive" Syntax highlighting for code in doctests and fixtures. | 1 | //! "Recursive" Syntax highlighting for code in doctests and fixtures. |
2 | 2 | ||
3 | use hir::Semantics; | 3 | use std::{mem, ops::Range}; |
4 | use ide_db::call_info::ActiveParameter; | ||
5 | use syntax::{ast, AstToken, SyntaxNode, SyntaxToken, TextRange, TextSize}; | ||
6 | 4 | ||
7 | use crate::{Analysis, HlMod, HlRange, HlTag, RootDatabase}; | 5 | use either::Either; |
6 | use hir::{HasAttrs, Semantics}; | ||
7 | use ide_db::{call_info::ActiveParameter, defs::Definition}; | ||
8 | use syntax::{ | ||
9 | ast::{self, AstNode, AttrsOwner, DocCommentsOwner}, | ||
10 | match_ast, AstToken, NodeOrToken, SyntaxNode, SyntaxToken, TextRange, TextSize, | ||
11 | }; | ||
12 | |||
13 | use crate::{ | ||
14 | doc_links::extract_definitions_from_markdown, Analysis, HlMod, HlRange, HlTag, RootDatabase, | ||
15 | }; | ||
8 | 16 | ||
9 | use super::{highlights::Highlights, injector::Injector}; | 17 | use super::{highlights::Highlights, injector::Injector}; |
10 | 18 | ||
@@ -81,70 +89,181 @@ const RUSTDOC_FENCE_TOKENS: &[&'static str] = &[ | |||
81 | "edition2021", | 89 | "edition2021", |
82 | ]; | 90 | ]; |
83 | 91 | ||
84 | /// Injection of syntax highlighting of doctests. | 92 | // Basically an owned dyn AttrsOwner without extra Boxing |
85 | pub(super) fn doc_comment(hl: &mut Highlights, node: &SyntaxNode) { | 93 | struct AttrsOwnerNode { |
86 | let doc_comments = node | 94 | node: SyntaxNode, |
87 | .children_with_tokens() | 95 | } |
88 | .filter_map(|it| it.into_token().and_then(ast::Comment::cast)) | 96 | |
89 | .filter(|it| it.kind().doc.is_some()); | 97 | impl AttrsOwnerNode { |
98 | fn new<N: DocCommentsOwner>(node: N) -> Self { | ||
99 | AttrsOwnerNode { node: node.syntax().clone() } | ||
100 | } | ||
101 | } | ||
102 | |||
103 | impl AttrsOwner for AttrsOwnerNode {} | ||
104 | impl AstNode for AttrsOwnerNode { | ||
105 | fn can_cast(_: syntax::SyntaxKind) -> bool | ||
106 | where | ||
107 | Self: Sized, | ||
108 | { | ||
109 | false | ||
110 | } | ||
111 | fn cast(_: SyntaxNode) -> Option<Self> | ||
112 | where | ||
113 | Self: Sized, | ||
114 | { | ||
115 | None | ||
116 | } | ||
117 | fn syntax(&self) -> &SyntaxNode { | ||
118 | &self.node | ||
119 | } | ||
120 | } | ||
90 | 121 | ||
91 | if !doc_comments.clone().any(|it| it.text().contains(RUSTDOC_FENCE)) { | 122 | fn doc_attributes<'node>( |
92 | return; | 123 | sema: &Semantics<RootDatabase>, |
124 | node: &'node SyntaxNode, | ||
125 | ) -> Option<(AttrsOwnerNode, hir::Attrs, Definition)> { | ||
126 | match_ast! { | ||
127 | match node { | ||
128 | ast::SourceFile(it) => sema.to_def(&it).map(|def| (AttrsOwnerNode::new(it), def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Module(def)))), | ||
129 | ast::Module(it) => sema.to_def(&it).map(|def| (AttrsOwnerNode::new(it), def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Module(def)))), | ||
130 | ast::Fn(it) => sema.to_def(&it).map(|def| (AttrsOwnerNode::new(it), def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Function(def)))), | ||
131 | ast::Struct(it) => sema.to_def(&it).map(|def| (AttrsOwnerNode::new(it), def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Struct(def))))), | ||
132 | ast::Union(it) => sema.to_def(&it).map(|def| (AttrsOwnerNode::new(it), def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Union(def))))), | ||
133 | ast::Enum(it) => sema.to_def(&it).map(|def| (AttrsOwnerNode::new(it), def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Enum(def))))), | ||
134 | ast::Variant(it) => sema.to_def(&it).map(|def| (AttrsOwnerNode::new(it), def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Variant(def)))), | ||
135 | ast::Trait(it) => sema.to_def(&it).map(|def| (AttrsOwnerNode::new(it), def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Trait(def)))), | ||
136 | ast::Static(it) => sema.to_def(&it).map(|def| (AttrsOwnerNode::new(it), def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Static(def)))), | ||
137 | ast::Const(it) => sema.to_def(&it).map(|def| (AttrsOwnerNode::new(it), def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Const(def)))), | ||
138 | ast::TypeAlias(it) => sema.to_def(&it).map(|def| (AttrsOwnerNode::new(it), def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::TypeAlias(def)))), | ||
139 | ast::Impl(it) => sema.to_def(&it).map(|def| (AttrsOwnerNode::new(it), def.attrs(sema.db), Definition::SelfType(def))), | ||
140 | ast::RecordField(it) => sema.to_def(&it).map(|def| (AttrsOwnerNode::new(it), def.attrs(sema.db), Definition::Field(def))), | ||
141 | ast::TupleField(it) => sema.to_def(&it).map(|def| (AttrsOwnerNode::new(it), def.attrs(sema.db), Definition::Field(def))), | ||
142 | ast::MacroRules(it) => sema.to_def(&it).map(|def| (AttrsOwnerNode::new(it), def.attrs(sema.db), Definition::Macro(def))), | ||
143 | // ast::MacroDef(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))), | ||
144 | // ast::Use(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))), | ||
145 | _ => return None | ||
146 | } | ||
93 | } | 147 | } |
148 | } | ||
149 | |||
150 | /// Injection of syntax highlighting of doctests. | ||
151 | pub(super) fn doc_comment(hl: &mut Highlights, sema: &Semantics<RootDatabase>, node: &SyntaxNode) { | ||
152 | let (owner, attributes, def) = match doc_attributes(sema, node) { | ||
153 | Some(it) => it, | ||
154 | None => return, | ||
155 | }; | ||
94 | 156 | ||
95 | let mut inj = Injector::default(); | 157 | let mut inj = Injector::default(); |
96 | inj.add_unmapped("fn doctest() {\n"); | 158 | inj.add_unmapped("fn doctest() {\n"); |
97 | 159 | ||
160 | let attrs_source_map = attributes.source_map(&owner); | ||
161 | |||
98 | let mut is_codeblock = false; | 162 | let mut is_codeblock = false; |
99 | let mut is_doctest = false; | 163 | let mut is_doctest = false; |
100 | 164 | ||
101 | // Replace the original, line-spanning comment ranges by new, only comment-prefix | 165 | // Replace the original, line-spanning comment ranges by new, only comment-prefix |
102 | // spanning comment ranges. | 166 | // spanning comment ranges. |
103 | let mut new_comments = Vec::new(); | 167 | let mut new_comments = Vec::new(); |
104 | for comment in doc_comments { | 168 | let mut intra_doc_links = Vec::new(); |
105 | match comment.text().find(RUSTDOC_FENCE) { | 169 | let mut string; |
106 | Some(idx) => { | 170 | for attr in attributes.by_key("doc").attrs() { |
107 | is_codeblock = !is_codeblock; | 171 | let src = attrs_source_map.source_of(&attr); |
108 | // Check whether code is rust by inspecting fence guards | 172 | let (line, range, prefix) = match &src { |
109 | let guards = &comment.text()[idx + RUSTDOC_FENCE.len()..]; | 173 | Either::Left(it) => { |
110 | let is_rust = | 174 | string = match find_doc_string_in_attr(attr, it) { |
111 | guards.split(',').all(|sub| RUSTDOC_FENCE_TOKENS.contains(&sub.trim())); | 175 | Some(it) => it, |
112 | is_doctest = is_codeblock && is_rust; | 176 | None => continue, |
113 | continue; | 177 | }; |
178 | let text_range = string.syntax().text_range(); | ||
179 | let text_range = TextRange::new( | ||
180 | text_range.start() + TextSize::from(1), | ||
181 | text_range.end() - TextSize::from(1), | ||
182 | ); | ||
183 | let text = string.text(); | ||
184 | (&text[1..text.len() - 1], text_range, "") | ||
114 | } | 185 | } |
115 | None if !is_doctest => continue, | 186 | Either::Right(comment) => { |
116 | None => (), | 187 | (comment.text(), comment.syntax().text_range(), comment.prefix()) |
117 | } | 188 | } |
189 | }; | ||
118 | 190 | ||
119 | let line: &str = comment.text(); | 191 | let mut pos = TextSize::from(prefix.len() as u32); |
120 | let range = comment.syntax().text_range(); | 192 | let mut range_start = range.start(); |
193 | for line in line.split('\n') { | ||
194 | let line_len = TextSize::from(line.len() as u32); | ||
195 | let prev_range_start = { | ||
196 | let next_range_start = range_start + line_len + TextSize::from(1); | ||
197 | mem::replace(&mut range_start, next_range_start) | ||
198 | }; | ||
199 | // only first line has the prefix so take it away for future iterations | ||
200 | let mut pos = mem::take(&mut pos); | ||
121 | 201 | ||
122 | let mut pos = TextSize::of(comment.prefix()); | 202 | match line.find(RUSTDOC_FENCE) { |
123 | // whitespace after comment is ignored | 203 | Some(idx) => { |
124 | if let Some(ws) = line[pos.into()..].chars().next().filter(|c| c.is_whitespace()) { | 204 | is_codeblock = !is_codeblock; |
125 | pos += TextSize::of(ws); | 205 | // Check whether code is rust by inspecting fence guards |
126 | } | 206 | let guards = &line[idx + RUSTDOC_FENCE.len()..]; |
127 | // lines marked with `#` should be ignored in output, we skip the `#` char | 207 | let is_rust = |
128 | if let Some(ws) = line[pos.into()..].chars().next().filter(|&c| c == '#') { | 208 | guards.split(',').all(|sub| RUSTDOC_FENCE_TOKENS.contains(&sub.trim())); |
129 | pos += TextSize::of(ws); | 209 | is_doctest = is_codeblock && is_rust; |
210 | continue; | ||
211 | } | ||
212 | None if !is_doctest => { | ||
213 | intra_doc_links.extend( | ||
214 | extract_definitions_from_markdown(line) | ||
215 | .into_iter() | ||
216 | .filter(|(link, ns, _)| { | ||
217 | validate_intra_doc_link(sema.db, &def, link, *ns) | ||
218 | }) | ||
219 | .map(|(.., Range { start, end })| { | ||
220 | TextRange::at( | ||
221 | prev_range_start + TextSize::from(start as u32), | ||
222 | TextSize::from((end - start) as u32), | ||
223 | ) | ||
224 | }), | ||
225 | ); | ||
226 | continue; | ||
227 | } | ||
228 | None => (), | ||
229 | } | ||
230 | |||
231 | // whitespace after comment is ignored | ||
232 | if let Some(ws) = line[pos.into()..].chars().next().filter(|c| c.is_whitespace()) { | ||
233 | pos += TextSize::of(ws); | ||
234 | } | ||
235 | // lines marked with `#` should be ignored in output, we skip the `#` char | ||
236 | if line[pos.into()..].starts_with('#') { | ||
237 | pos += TextSize::of('#'); | ||
238 | } | ||
239 | |||
240 | new_comments.push(TextRange::at(prev_range_start, pos)); | ||
241 | inj.add(&line[pos.into()..], TextRange::new(pos, line_len) + prev_range_start); | ||
242 | inj.add_unmapped("\n"); | ||
130 | } | 243 | } |
244 | } | ||
131 | 245 | ||
132 | new_comments.push(TextRange::at(range.start(), pos)); | 246 | for range in intra_doc_links { |
247 | hl.add(HlRange { | ||
248 | range, | ||
249 | highlight: HlTag::IntraDocLink | HlMod::Documentation, | ||
250 | binding_hash: None, | ||
251 | }); | ||
252 | } | ||
133 | 253 | ||
134 | inj.add(&line[pos.into()..], TextRange::new(range.start() + pos, range.end())); | 254 | if new_comments.is_empty() { |
135 | inj.add_unmapped("\n"); | 255 | return; // no need to run an analysis on an empty file |
136 | } | 256 | } |
257 | |||
137 | inj.add_unmapped("\n}"); | 258 | inj.add_unmapped("\n}"); |
138 | 259 | ||
139 | let (analysis, tmp_file_id) = Analysis::from_single_file(inj.text().to_string()); | 260 | let (analysis, tmp_file_id) = Analysis::from_single_file(inj.text().to_string()); |
140 | 261 | ||
141 | for h in analysis.with_db(|db| super::highlight(db, tmp_file_id, None, true)).unwrap() { | 262 | for HlRange { range, highlight, binding_hash } in |
142 | for r in inj.map_range_up(h.range) { | 263 | analysis.with_db(|db| super::highlight(db, tmp_file_id, None, true)).unwrap() |
143 | hl.add(HlRange { | 264 | { |
144 | range: r, | 265 | for range in inj.map_range_up(range) { |
145 | highlight: h.highlight | HlMod::Injected, | 266 | hl.add(HlRange { range, highlight: highlight | HlMod::Injected, binding_hash }); |
146 | binding_hash: h.binding_hash, | ||
147 | }); | ||
148 | } | 267 | } |
149 | } | 268 | } |
150 | 269 | ||
@@ -156,3 +275,55 @@ pub(super) fn doc_comment(hl: &mut Highlights, node: &SyntaxNode) { | |||
156 | }); | 275 | }); |
157 | } | 276 | } |
158 | } | 277 | } |
278 | |||
279 | fn find_doc_string_in_attr(attr: &hir::Attr, it: &ast::Attr) -> Option<ast::String> { | ||
280 | match it.literal() { | ||
281 | // #[doc = lit] | ||
282 | Some(lit) => match lit.kind() { | ||
283 | ast::LiteralKind::String(it) => Some(it), | ||
284 | _ => None, | ||
285 | }, | ||
286 | // #[cfg_attr(..., doc = "", ...)] | ||
287 | None => { | ||
288 | // We gotta hunt the string token manually here | ||
289 | let text = attr.string_value()?; | ||
290 | // FIXME: We just pick the first string literal that has the same text as the doc attribute | ||
291 | // This means technically we might highlight the wrong one | ||
292 | it.syntax() | ||
293 | .descendants_with_tokens() | ||
294 | .filter_map(NodeOrToken::into_token) | ||
295 | .filter_map(ast::String::cast) | ||
296 | .find(|string| { | ||
297 | string.text().get(1..string.text().len() - 1).map_or(false, |it| it == text) | ||
298 | }) | ||
299 | } | ||
300 | } | ||
301 | } | ||
302 | |||
303 | fn validate_intra_doc_link( | ||
304 | db: &RootDatabase, | ||
305 | def: &Definition, | ||
306 | link: &str, | ||
307 | ns: Option<hir::Namespace>, | ||
308 | ) -> bool { | ||
309 | match def { | ||
310 | Definition::ModuleDef(def) => match def { | ||
311 | hir::ModuleDef::Module(it) => it.resolve_doc_path(db, &link, ns), | ||
312 | hir::ModuleDef::Function(it) => it.resolve_doc_path(db, &link, ns), | ||
313 | hir::ModuleDef::Adt(it) => it.resolve_doc_path(db, &link, ns), | ||
314 | hir::ModuleDef::Variant(it) => it.resolve_doc_path(db, &link, ns), | ||
315 | hir::ModuleDef::Const(it) => it.resolve_doc_path(db, &link, ns), | ||
316 | hir::ModuleDef::Static(it) => it.resolve_doc_path(db, &link, ns), | ||
317 | hir::ModuleDef::Trait(it) => it.resolve_doc_path(db, &link, ns), | ||
318 | hir::ModuleDef::TypeAlias(it) => it.resolve_doc_path(db, &link, ns), | ||
319 | hir::ModuleDef::BuiltinType(_) => None, | ||
320 | }, | ||
321 | Definition::Macro(it) => it.resolve_doc_path(db, &link, ns), | ||
322 | Definition::Field(it) => it.resolve_doc_path(db, &link, ns), | ||
323 | Definition::SelfType(_) | ||
324 | | Definition::Local(_) | ||
325 | | Definition::GenericParam(_) | ||
326 | | Definition::Label(_) => None, | ||
327 | } | ||
328 | .is_some() | ||
329 | } | ||
diff --git a/crates/ide/src/syntax_highlighting/tags.rs b/crates/ide/src/syntax_highlighting/tags.rs index 3c02fdb11..ce46e5127 100644 --- a/crates/ide/src/syntax_highlighting/tags.rs +++ b/crates/ide/src/syntax_highlighting/tags.rs | |||
@@ -18,19 +18,20 @@ pub struct HlMods(u32); | |||
18 | pub enum HlTag { | 18 | pub enum HlTag { |
19 | Symbol(SymbolKind), | 19 | Symbol(SymbolKind), |
20 | 20 | ||
21 | Attribute, | ||
21 | BoolLiteral, | 22 | BoolLiteral, |
22 | BuiltinType, | 23 | BuiltinType, |
23 | ByteLiteral, | 24 | ByteLiteral, |
24 | CharLiteral, | 25 | CharLiteral, |
25 | NumericLiteral, | ||
26 | StringLiteral, | ||
27 | Attribute, | ||
28 | Comment, | 26 | Comment, |
29 | EscapeSequence, | 27 | EscapeSequence, |
30 | FormatSpecifier, | 28 | FormatSpecifier, |
29 | IntraDocLink, | ||
31 | Keyword, | 30 | Keyword, |
32 | Punctuation(HlPunct), | 31 | NumericLiteral, |
33 | Operator, | 32 | Operator, |
33 | Punctuation(HlPunct), | ||
34 | StringLiteral, | ||
34 | UnresolvedReference, | 35 | UnresolvedReference, |
35 | 36 | ||
36 | // For things which don't have a specific highlight. | 37 | // For things which don't have a specific highlight. |
@@ -116,6 +117,7 @@ impl HlTag { | |||
116 | HlTag::Comment => "comment", | 117 | HlTag::Comment => "comment", |
117 | HlTag::EscapeSequence => "escape_sequence", | 118 | HlTag::EscapeSequence => "escape_sequence", |
118 | HlTag::FormatSpecifier => "format_specifier", | 119 | HlTag::FormatSpecifier => "format_specifier", |
120 | HlTag::IntraDocLink => "intra_doc_link", | ||
119 | HlTag::Keyword => "keyword", | 121 | HlTag::Keyword => "keyword", |
120 | HlTag::Punctuation(punct) => match punct { | 122 | HlTag::Punctuation(punct) => match punct { |
121 | HlPunct::Bracket => "bracket", | 123 | HlPunct::Bracket => "bracket", |
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html b/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html index d421a7803..60c7518af 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html | |||
@@ -7,6 +7,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
7 | .label { color: #DFAF8F; font-style: italic; } | 7 | .label { color: #DFAF8F; font-style: italic; } |
8 | .comment { color: #7F9F7F; } | 8 | .comment { color: #7F9F7F; } |
9 | .documentation { color: #629755; } | 9 | .documentation { color: #629755; } |
10 | .intra_doc_link { color: #A9C577; } | ||
10 | .injected { opacity: 0.65 ; } | 11 | .injected { opacity: 0.65 ; } |
11 | .struct, .enum { color: #7CB8BB; } | 12 | .struct, .enum { color: #7CB8BB; } |
12 | .enum_variant { color: #BDE0F3; } | 13 | .enum_variant { color: #BDE0F3; } |
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html index 5e877df88..5d802a647 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html | |||
@@ -7,6 +7,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
7 | .label { color: #DFAF8F; font-style: italic; } | 7 | .label { color: #DFAF8F; font-style: italic; } |
8 | .comment { color: #7F9F7F; } | 8 | .comment { color: #7F9F7F; } |
9 | .documentation { color: #629755; } | 9 | .documentation { color: #629755; } |
10 | .intra_doc_link { color: #A9C577; } | ||
10 | .injected { opacity: 0.65 ; } | 11 | .injected { opacity: 0.65 ; } |
11 | .struct, .enum { color: #7CB8BB; } | 12 | .struct, .enum { color: #7CB8BB; } |
12 | .enum_variant { color: #BDE0F3; } | 13 | .enum_variant { color: #BDE0F3; } |
@@ -81,7 +82,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
81 | <span class="comment documentation">/// </span><span class="comment injected"> comment */</span> | 82 | <span class="comment documentation">/// </span><span class="comment injected"> comment */</span> |
82 | <span class="comment documentation">///</span> | 83 | <span class="comment documentation">///</span> |
83 | <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="variable declaration injected">multi_line_string</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="string_literal injected">"Foo</span> | 84 | <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="variable declaration injected">multi_line_string</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="string_literal injected">"Foo</span> |
84 | <span class="comment documentation">/// </span><span class="string_literal injected"> bar</span> | 85 | <span class="comment documentation">/// </span><span class="string_literal injected"> bar</span><span class="escape_sequence injected">\n</span> |
85 | <span class="comment documentation">/// </span><span class="string_literal injected"> "</span><span class="semicolon injected">;</span> | 86 | <span class="comment documentation">/// </span><span class="string_literal injected"> "</span><span class="semicolon injected">;</span> |
86 | <span class="comment documentation">///</span> | 87 | <span class="comment documentation">///</span> |
87 | <span class="comment documentation">/// ```</span> | 88 | <span class="comment documentation">/// ```</span> |
@@ -98,6 +99,11 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
98 | <span class="brace">}</span> | 99 | <span class="brace">}</span> |
99 | <span class="brace">}</span> | 100 | <span class="brace">}</span> |
100 | 101 | ||
102 | <span class="comment documentation">/// </span><span class="intra_doc_link documentation">[`Foo`](Foo)</span><span class="comment documentation"> is a struct</span> | ||
103 | <span class="comment documentation">/// </span><span class="intra_doc_link documentation">[`all_the_links`](all_the_links)</span><span class="comment documentation"> is this function</span> | ||
104 | <span class="comment documentation">/// [`noop`](noop) is a macro below</span> | ||
105 | <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration">all_the_links</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> | ||
106 | |||
101 | <span class="comment documentation">/// ```</span> | 107 | <span class="comment documentation">/// ```</span> |
102 | <span class="comment documentation">/// </span><span class="macro injected">noop!</span><span class="parenthesis injected">(</span><span class="numeric_literal injected">1</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span> | 108 | <span class="comment documentation">/// </span><span class="macro injected">noop!</span><span class="parenthesis injected">(</span><span class="numeric_literal injected">1</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span> |
103 | <span class="comment documentation">/// ```</span> | 109 | <span class="comment documentation">/// ```</span> |
@@ -105,4 +111,36 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
105 | <span class="parenthesis">(</span><span class="punctuation">$</span>expr<span class="colon">:</span>expr<span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">></span> <span class="brace">{</span> | 111 | <span class="parenthesis">(</span><span class="punctuation">$</span>expr<span class="colon">:</span>expr<span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">></span> <span class="brace">{</span> |
106 | <span class="punctuation">$</span>expr | 112 | <span class="punctuation">$</span>expr |
107 | <span class="brace">}</span> | 113 | <span class="brace">}</span> |
108 | <span class="brace">}</span></code></pre> \ No newline at end of file | 114 | <span class="brace">}</span> |
115 | |||
116 | <span class="comment documentation">/// ```rust</span> | ||
117 | <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="punctuation injected">_</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="function injected">example</span><span class="parenthesis injected">(</span><span class="operator injected">&</span><span class="bracket injected">[</span><span class="numeric_literal injected">1</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">2</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">3</span><span class="bracket injected">]</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span> | ||
118 | <span class="comment documentation">/// ```</span> | ||
119 | <span class="comment documentation">///</span> | ||
120 | <span class="comment documentation">/// ```</span> | ||
121 | <span class="comment documentation">/// </span><span class="keyword control injected">loop</span><span class="none injected"> </span><span class="brace injected">{</span><span class="brace injected">}</span> | ||
122 | <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">cfg_attr</span><span class="parenthesis attribute">(</span><span class="attribute attribute">not</span><span class="parenthesis attribute">(</span><span class="attribute attribute">feature </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"false"</span><span class="parenthesis attribute">)</span><span class="comma attribute">,</span><span class="attribute attribute"> doc </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"</span><span class="keyword control injected">loop</span><span class="none injected"> </span><span class="brace injected">{</span><span class="brace injected">}</span><span class="string_literal attribute">"</span><span class="parenthesis attribute">)</span><span class="attribute attribute">]</span> | ||
123 | <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">doc</span><span class="attribute attribute"> </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"</span><span class="keyword control injected">loop</span><span class="none injected"> </span><span class="brace injected">{</span><span class="brace injected">}</span><span class="string_literal attribute">"</span><span class="attribute attribute">]</span> | ||
124 | <span class="comment documentation">/// ```</span> | ||
125 | <span class="comment documentation">///</span> | ||
126 | <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">cfg_attr</span><span class="parenthesis attribute">(</span><span class="attribute attribute">feature </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"alloc"</span><span class="comma attribute">,</span><span class="attribute attribute"> doc </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"```rust"</span><span class="parenthesis attribute">)</span><span class="attribute attribute">]</span> | ||
127 | <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">cfg_attr</span><span class="parenthesis attribute">(</span><span class="attribute attribute">not</span><span class="parenthesis attribute">(</span><span class="attribute attribute">feature </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"alloc"</span><span class="parenthesis attribute">)</span><span class="comma attribute">,</span><span class="attribute attribute"> doc </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"```ignore"</span><span class="parenthesis attribute">)</span><span class="attribute attribute">]</span> | ||
128 | <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="punctuation injected">_</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="function injected">example</span><span class="parenthesis injected">(</span><span class="operator injected">&</span><span class="none injected">alloc::</span><span class="macro injected">vec!</span><span class="bracket injected">[</span><span class="numeric_literal injected">1</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">2</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">3</span><span class="bracket injected">]</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span> | ||
129 | <span class="comment documentation">/// ```</span> | ||
130 | <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration">mix_and_match</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> | ||
131 | |||
132 | <span class="comment documentation">/** | ||
133 | It is beyond me why you'd use these when you got /// | ||
134 | ```rust | ||
135 | </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="punctuation injected">_</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="function injected">example</span><span class="parenthesis injected">(</span><span class="operator injected">&</span><span class="bracket injected">[</span><span class="numeric_literal injected">1</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">2</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">3</span><span class="bracket injected">]</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span><span class="comment documentation"> | ||
136 | ``` | ||
137 | */</span> | ||
138 | <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration">block_comments</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> | ||
139 | |||
140 | <span class="comment documentation">/** | ||
141 | Really, I don't get it | ||
142 | ```rust | ||
143 | </span><span class="comment documentation"> </span><span class="none injected"> </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="punctuation injected">_</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="function injected">example</span><span class="parenthesis injected">(</span><span class="operator injected">&</span><span class="bracket injected">[</span><span class="numeric_literal injected">1</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">2</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">3</span><span class="bracket injected">]</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span><span class="comment documentation"> | ||
144 | ``` | ||
145 | */</span> | ||
146 | <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration">block_comments2</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span></code></pre> \ No newline at end of file | ||
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html b/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html index 6f7a7ffff..4e312765c 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html | |||
@@ -7,6 +7,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
7 | .label { color: #DFAF8F; font-style: italic; } | 7 | .label { color: #DFAF8F; font-style: italic; } |
8 | .comment { color: #7F9F7F; } | 8 | .comment { color: #7F9F7F; } |
9 | .documentation { color: #629755; } | 9 | .documentation { color: #629755; } |
10 | .intra_doc_link { color: #A9C577; } | ||
10 | .injected { opacity: 0.65 ; } | 11 | .injected { opacity: 0.65 ; } |
11 | .struct, .enum { color: #7CB8BB; } | 12 | .struct, .enum { color: #7CB8BB; } |
12 | .enum_variant { color: #BDE0F3; } | 13 | .enum_variant { color: #BDE0F3; } |
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html b/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html index 753b535b5..57dfe7509 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html | |||
@@ -7,6 +7,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
7 | .label { color: #DFAF8F; font-style: italic; } | 7 | .label { color: #DFAF8F; font-style: italic; } |
8 | .comment { color: #7F9F7F; } | 8 | .comment { color: #7F9F7F; } |
9 | .documentation { color: #629755; } | 9 | .documentation { color: #629755; } |
10 | .intra_doc_link { color: #A9C577; } | ||
10 | .injected { opacity: 0.65 ; } | 11 | .injected { opacity: 0.65 ; } |
11 | .struct, .enum { color: #7CB8BB; } | 12 | .struct, .enum { color: #7CB8BB; } |
12 | .enum_variant { color: #BDE0F3; } | 13 | .enum_variant { color: #BDE0F3; } |
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html index 66d80c4b6..75dbd0f14 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html | |||
@@ -7,6 +7,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
7 | .label { color: #DFAF8F; font-style: italic; } | 7 | .label { color: #DFAF8F; font-style: italic; } |
8 | .comment { color: #7F9F7F; } | 8 | .comment { color: #7F9F7F; } |
9 | .documentation { color: #629755; } | 9 | .documentation { color: #629755; } |
10 | .intra_doc_link { color: #A9C577; } | ||
10 | .injected { opacity: 0.65 ; } | 11 | .injected { opacity: 0.65 ; } |
11 | .struct, .enum { color: #7CB8BB; } | 12 | .struct, .enum { color: #7CB8BB; } |
12 | .enum_variant { color: #BDE0F3; } | 13 | .enum_variant { color: #BDE0F3; } |
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html index 036cb6c11..423256a20 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html | |||
@@ -7,6 +7,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
7 | .label { color: #DFAF8F; font-style: italic; } | 7 | .label { color: #DFAF8F; font-style: italic; } |
8 | .comment { color: #7F9F7F; } | 8 | .comment { color: #7F9F7F; } |
9 | .documentation { color: #629755; } | 9 | .documentation { color: #629755; } |
10 | .intra_doc_link { color: #A9C577; } | ||
10 | .injected { opacity: 0.65 ; } | 11 | .injected { opacity: 0.65 ; } |
11 | .struct, .enum { color: #7CB8BB; } | 12 | .struct, .enum { color: #7CB8BB; } |
12 | .enum_variant { color: #BDE0F3; } | 13 | .enum_variant { color: #BDE0F3; } |
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlighting.html b/crates/ide/src/syntax_highlighting/test_data/highlighting.html index 2f983c0b8..fffe8c0f5 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlighting.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlighting.html | |||
@@ -7,6 +7,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
7 | .label { color: #DFAF8F; font-style: italic; } | 7 | .label { color: #DFAF8F; font-style: italic; } |
8 | .comment { color: #7F9F7F; } | 8 | .comment { color: #7F9F7F; } |
9 | .documentation { color: #629755; } | 9 | .documentation { color: #629755; } |
10 | .intra_doc_link { color: #A9C577; } | ||
10 | .injected { opacity: 0.65 ; } | 11 | .injected { opacity: 0.65 ; } |
11 | .struct, .enum { color: #7CB8BB; } | 12 | .struct, .enum { color: #7CB8BB; } |
12 | .enum_variant { color: #BDE0F3; } | 13 | .enum_variant { color: #BDE0F3; } |
diff --git a/crates/ide/src/syntax_highlighting/test_data/injection.html b/crates/ide/src/syntax_highlighting/test_data/injection.html index 78dfec951..34d8deb68 100644 --- a/crates/ide/src/syntax_highlighting/test_data/injection.html +++ b/crates/ide/src/syntax_highlighting/test_data/injection.html | |||
@@ -7,6 +7,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
7 | .label { color: #DFAF8F; font-style: italic; } | 7 | .label { color: #DFAF8F; font-style: italic; } |
8 | .comment { color: #7F9F7F; } | 8 | .comment { color: #7F9F7F; } |
9 | .documentation { color: #629755; } | 9 | .documentation { color: #629755; } |
10 | .intra_doc_link { color: #A9C577; } | ||
10 | .injected { opacity: 0.65 ; } | 11 | .injected { opacity: 0.65 ; } |
11 | .struct, .enum { color: #7CB8BB; } | 12 | .struct, .enum { color: #7CB8BB; } |
12 | .enum_variant { color: #BDE0F3; } | 13 | .enum_variant { color: #BDE0F3; } |
diff --git a/crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html b/crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html index e64f2e5e9..d9ca3a4c4 100644 --- a/crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html +++ b/crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html | |||
@@ -7,6 +7,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
7 | .label { color: #DFAF8F; font-style: italic; } | 7 | .label { color: #DFAF8F; font-style: italic; } |
8 | .comment { color: #7F9F7F; } | 8 | .comment { color: #7F9F7F; } |
9 | .documentation { color: #629755; } | 9 | .documentation { color: #629755; } |
10 | .intra_doc_link { color: #A9C577; } | ||
10 | .injected { opacity: 0.65 ; } | 11 | .injected { opacity: 0.65 ; } |
11 | .struct, .enum { color: #7CB8BB; } | 12 | .struct, .enum { color: #7CB8BB; } |
12 | .enum_variant { color: #BDE0F3; } | 13 | .enum_variant { color: #BDE0F3; } |
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs index 9d0cd1af5..7b2922b0d 100644 --- a/crates/ide/src/syntax_highlighting/tests.rs +++ b/crates/ide/src/syntax_highlighting/tests.rs | |||
@@ -468,7 +468,7 @@ fn main() { | |||
468 | } | 468 | } |
469 | 469 | ||
470 | #[test] | 470 | #[test] |
471 | fn test_highlight_doctest() { | 471 | fn test_highlight_doc_comment() { |
472 | check_highlighting( | 472 | check_highlighting( |
473 | r#" | 473 | r#" |
474 | /// ``` | 474 | /// ``` |
@@ -516,7 +516,7 @@ impl Foo { | |||
516 | /// comment */ | 516 | /// comment */ |
517 | /// | 517 | /// |
518 | /// let multi_line_string = "Foo | 518 | /// let multi_line_string = "Foo |
519 | /// bar | 519 | /// bar\n |
520 | /// "; | 520 | /// "; |
521 | /// | 521 | /// |
522 | /// ``` | 522 | /// ``` |
@@ -533,6 +533,11 @@ impl Foo { | |||
533 | } | 533 | } |
534 | } | 534 | } |
535 | 535 | ||
536 | /// [`Foo`](Foo) is a struct | ||
537 | /// [`all_the_links`](all_the_links) is this function | ||
538 | /// [`noop`](noop) is a macro below | ||
539 | pub fn all_the_links() {} | ||
540 | |||
536 | /// ``` | 541 | /// ``` |
537 | /// noop!(1); | 542 | /// noop!(1); |
538 | /// ``` | 543 | /// ``` |
@@ -541,6 +546,38 @@ macro_rules! noop { | |||
541 | $expr | 546 | $expr |
542 | } | 547 | } |
543 | } | 548 | } |
549 | |||
550 | /// ```rust | ||
551 | /// let _ = example(&[1, 2, 3]); | ||
552 | /// ``` | ||
553 | /// | ||
554 | /// ``` | ||
555 | /// loop {} | ||
556 | #[cfg_attr(not(feature = "false"), doc = "loop {}")] | ||
557 | #[doc = "loop {}"] | ||
558 | /// ``` | ||
559 | /// | ||
560 | #[cfg_attr(feature = "alloc", doc = "```rust")] | ||
561 | #[cfg_attr(not(feature = "alloc"), doc = "```ignore")] | ||
562 | /// let _ = example(&alloc::vec![1, 2, 3]); | ||
563 | /// ``` | ||
564 | pub fn mix_and_match() {} | ||
565 | |||
566 | /** | ||
567 | It is beyond me why you'd use these when you got /// | ||
568 | ```rust | ||
569 | let _ = example(&[1, 2, 3]); | ||
570 | ``` | ||
571 | */ | ||
572 | pub fn block_comments() {} | ||
573 | |||
574 | /** | ||
575 | Really, I don't get it | ||
576 | ```rust | ||
577 | let _ = example(&[1, 2, 3]); | ||
578 | ``` | ||
579 | */ | ||
580 | pub fn block_comments2() {} | ||
544 | "# | 581 | "# |
545 | .trim(), | 582 | .trim(), |
546 | expect_file!["./test_data/highlight_doctest.html"], | 583 | expect_file!["./test_data/highlight_doctest.html"], |
diff --git a/crates/ide_completion/src/lib.rs b/crates/ide_completion/src/lib.rs index 5b7ad38d5..d9ea7b7ea 100644 --- a/crates/ide_completion/src/lib.rs +++ b/crates/ide_completion/src/lib.rs | |||
@@ -255,7 +255,7 @@ fn foo() { | |||
255 | bar.fo$0; | 255 | bar.fo$0; |
256 | } | 256 | } |
257 | "#, | 257 | "#, |
258 | DetailAndDocumentation { detail: "fn(&self)", documentation: " Do the foo" }, | 258 | DetailAndDocumentation { detail: "fn(&self)", documentation: "Do the foo" }, |
259 | ); | 259 | ); |
260 | } | 260 | } |
261 | 261 | ||
diff --git a/crates/ide_db/src/apply_change.rs b/crates/ide_db/src/apply_change.rs index 104ee113f..9b590d919 100644 --- a/crates/ide_db/src/apply_change.rs +++ b/crates/ide_db/src/apply_change.rs | |||
@@ -148,7 +148,7 @@ impl RootDatabase { | |||
148 | hir::db::HygieneFrameQuery | 148 | hir::db::HygieneFrameQuery |
149 | 149 | ||
150 | // DefDatabase | 150 | // DefDatabase |
151 | hir::db::ItemTreeQuery | 151 | hir::db::FileItemTreeQuery |
152 | hir::db::BlockDefMapQuery | 152 | hir::db::BlockDefMapQuery |
153 | hir::db::CrateDefMapQueryQuery | 153 | hir::db::CrateDefMapQueryQuery |
154 | hir::db::StructDataQuery | 154 | hir::db::StructDataQuery |
diff --git a/crates/ide_db/src/call_info.rs b/crates/ide_db/src/call_info.rs index d4016973c..7e26c3ccf 100644 --- a/crates/ide_db/src/call_info.rs +++ b/crates/ide_db/src/call_info.rs | |||
@@ -53,15 +53,15 @@ pub fn call_info(db: &RootDatabase, position: FilePosition) -> Option<CallInfo> | |||
53 | 53 | ||
54 | match callable.kind() { | 54 | match callable.kind() { |
55 | hir::CallableKind::Function(func) => { | 55 | hir::CallableKind::Function(func) => { |
56 | res.doc = func.docs(db).map(|it| it.as_str().to_string()); | 56 | res.doc = func.docs(db).map(|it| it.into()); |
57 | format_to!(res.signature, "fn {}", func.name(db)); | 57 | format_to!(res.signature, "fn {}", func.name(db)); |
58 | } | 58 | } |
59 | hir::CallableKind::TupleStruct(strukt) => { | 59 | hir::CallableKind::TupleStruct(strukt) => { |
60 | res.doc = strukt.docs(db).map(|it| it.as_str().to_string()); | 60 | res.doc = strukt.docs(db).map(|it| it.into()); |
61 | format_to!(res.signature, "struct {}", strukt.name(db)); | 61 | format_to!(res.signature, "struct {}", strukt.name(db)); |
62 | } | 62 | } |
63 | hir::CallableKind::TupleEnumVariant(variant) => { | 63 | hir::CallableKind::TupleEnumVariant(variant) => { |
64 | res.doc = variant.docs(db).map(|it| it.as_str().to_string()); | 64 | res.doc = variant.docs(db).map(|it| it.into()); |
65 | format_to!( | 65 | format_to!( |
66 | res.signature, | 66 | res.signature, |
67 | "enum {}::{}", | 67 | "enum {}::{}", |
diff --git a/crates/ide_db/src/call_info/tests.rs b/crates/ide_db/src/call_info/tests.rs index 9f84c253c..75ab3eb6e 100644 --- a/crates/ide_db/src/call_info/tests.rs +++ b/crates/ide_db/src/call_info/tests.rs | |||
@@ -220,11 +220,11 @@ fn bar() { | |||
220 | } | 220 | } |
221 | "#, | 221 | "#, |
222 | expect![[r#" | 222 | expect![[r#" |
223 | test | 223 | test |
224 | ------ | 224 | ------ |
225 | fn foo(j: u32) -> u32 | 225 | fn foo(j: u32) -> u32 |
226 | (<j: u32>) | 226 | (<j: u32>) |
227 | "#]], | 227 | "#]], |
228 | ); | 228 | ); |
229 | } | 229 | } |
230 | 230 | ||
@@ -249,19 +249,19 @@ pub fn do() { | |||
249 | add_one($0 | 249 | add_one($0 |
250 | }"#, | 250 | }"#, |
251 | expect![[r##" | 251 | expect![[r##" |
252 | Adds one to the number given. | 252 | Adds one to the number given. |
253 | 253 | ||
254 | # Examples | 254 | # Examples |
255 | 255 | ||
256 | ``` | 256 | ``` |
257 | let five = 5; | 257 | let five = 5; |
258 | 258 | ||
259 | assert_eq!(6, my_crate::add_one(5)); | 259 | assert_eq!(6, my_crate::add_one(5)); |
260 | ``` | 260 | ``` |
261 | ------ | 261 | ------ |
262 | fn add_one(x: i32) -> i32 | 262 | fn add_one(x: i32) -> i32 |
263 | (<x: i32>) | 263 | (<x: i32>) |
264 | "##]], | 264 | "##]], |
265 | ); | 265 | ); |
266 | } | 266 | } |
267 | 267 | ||
@@ -291,19 +291,19 @@ pub fn do_it() { | |||
291 | } | 291 | } |
292 | "#, | 292 | "#, |
293 | expect![[r##" | 293 | expect![[r##" |
294 | Adds one to the number given. | 294 | Adds one to the number given. |
295 | 295 | ||
296 | # Examples | 296 | # Examples |
297 | 297 | ||
298 | ``` | 298 | ``` |
299 | let five = 5; | 299 | let five = 5; |
300 | 300 | ||
301 | assert_eq!(6, my_crate::add_one(5)); | 301 | assert_eq!(6, my_crate::add_one(5)); |
302 | ``` | 302 | ``` |
303 | ------ | 303 | ------ |
304 | fn add_one(x: i32) -> i32 | 304 | fn add_one(x: i32) -> i32 |
305 | (<x: i32>) | 305 | (<x: i32>) |
306 | "##]], | 306 | "##]], |
307 | ); | 307 | ); |
308 | } | 308 | } |
309 | 309 | ||
@@ -335,13 +335,13 @@ pub fn foo(mut r: WriteHandler<()>) { | |||
335 | } | 335 | } |
336 | "#, | 336 | "#, |
337 | expect![[r#" | 337 | expect![[r#" |
338 | Method is called when writer finishes. | 338 | Method is called when writer finishes. |
339 | 339 | ||
340 | By default this method stops actor's `Context`. | 340 | By default this method stops actor's `Context`. |
341 | ------ | 341 | ------ |
342 | fn finished(&mut self, ctx: &mut {unknown}) | 342 | fn finished(&mut self, ctx: &mut {unknown}) |
343 | (<ctx: &mut {unknown}>) | 343 | (<ctx: &mut {unknown}>) |
344 | "#]], | 344 | "#]], |
345 | ); | 345 | ); |
346 | } | 346 | } |
347 | 347 | ||
@@ -389,11 +389,11 @@ fn main() { | |||
389 | } | 389 | } |
390 | "#, | 390 | "#, |
391 | expect![[r#" | 391 | expect![[r#" |
392 | A cool tuple struct | 392 | A cool tuple struct |
393 | ------ | 393 | ------ |
394 | struct S(u32, i32) | 394 | struct S(u32, i32) |
395 | (u32, <i32>) | 395 | (u32, <i32>) |
396 | "#]], | 396 | "#]], |
397 | ); | 397 | ); |
398 | } | 398 | } |
399 | 399 | ||
@@ -431,11 +431,11 @@ fn main() { | |||
431 | } | 431 | } |
432 | "#, | 432 | "#, |
433 | expect![[r#" | 433 | expect![[r#" |
434 | A Variant | 434 | A Variant |
435 | ------ | 435 | ------ |
436 | enum E::A(i32) | 436 | enum E::A(i32) |
437 | (<i32>) | 437 | (<i32>) |
438 | "#]], | 438 | "#]], |
439 | ); | 439 | ); |
440 | } | 440 | } |
441 | 441 | ||
diff --git a/crates/mbe/src/tests.rs b/crates/mbe/src/tests.rs index 25c374b9b..6da18ecf4 100644 --- a/crates/mbe/src/tests.rs +++ b/crates/mbe/src/tests.rs | |||
@@ -1,1775 +1,14 @@ | |||
1 | mod expand; | ||
2 | mod rule; | ||
3 | |||
1 | use std::fmt::Write; | 4 | use std::fmt::Write; |
2 | 5 | ||
3 | use ::parser::FragmentKind; | 6 | use ::parser::FragmentKind; |
4 | use syntax::{ | 7 | use syntax::{ast, AstNode, NodeOrToken, SyntaxNode, WalkEvent}; |
5 | ast, AstNode, NodeOrToken, | ||
6 | SyntaxKind::{ERROR, IDENT}, | ||
7 | SyntaxNode, WalkEvent, T, | ||
8 | }; | ||
9 | use test_utils::assert_eq_text; | 8 | use test_utils::assert_eq_text; |
10 | 9 | ||
11 | use super::*; | 10 | use super::*; |
12 | 11 | ||
13 | mod rule_parsing { | ||
14 | use syntax::{ast, AstNode}; | ||
15 | |||
16 | use crate::ast_to_token_tree; | ||
17 | |||
18 | use super::*; | ||
19 | |||
20 | #[test] | ||
21 | fn test_valid_arms() { | ||
22 | fn check(macro_body: &str) { | ||
23 | let m = parse_macro_arm(macro_body); | ||
24 | m.unwrap(); | ||
25 | } | ||
26 | |||
27 | check("($i:ident) => ()"); | ||
28 | check("($($i:ident)*) => ($_)"); | ||
29 | check("($($true:ident)*) => ($true)"); | ||
30 | check("($($false:ident)*) => ($false)"); | ||
31 | check("($) => ($)"); | ||
32 | } | ||
33 | |||
34 | #[test] | ||
35 | fn test_invalid_arms() { | ||
36 | fn check(macro_body: &str, err: ParseError) { | ||
37 | let m = parse_macro_arm(macro_body); | ||
38 | assert_eq!(m, Err(err)); | ||
39 | } | ||
40 | check("invalid", ParseError::Expected("expected subtree".into())); | ||
41 | |||
42 | check("$i:ident => ()", ParseError::Expected("expected subtree".into())); | ||
43 | check("($i:ident) ()", ParseError::Expected("expected `=`".into())); | ||
44 | check("($($i:ident)_) => ()", ParseError::InvalidRepeat); | ||
45 | |||
46 | check("($i) => ($i)", ParseError::UnexpectedToken("bad fragment specifier 1".into())); | ||
47 | check("($i:) => ($i)", ParseError::UnexpectedToken("bad fragment specifier 1".into())); | ||
48 | } | ||
49 | |||
50 | fn parse_macro_arm(arm_definition: &str) -> Result<crate::MacroRules, ParseError> { | ||
51 | let macro_definition = format!(" macro_rules! m {{ {} }} ", arm_definition); | ||
52 | let source_file = ast::SourceFile::parse(¯o_definition).ok().unwrap(); | ||
53 | let macro_definition = | ||
54 | source_file.syntax().descendants().find_map(ast::MacroRules::cast).unwrap(); | ||
55 | |||
56 | let (definition_tt, _) = | ||
57 | ast_to_token_tree(¯o_definition.token_tree().unwrap()).unwrap(); | ||
58 | crate::MacroRules::parse(&definition_tt) | ||
59 | } | ||
60 | } | ||
61 | |||
62 | // Good first issue (although a slightly challenging one): | ||
63 | // | ||
64 | // * Pick a random test from here | ||
65 | // https://github.com/intellij-rust/intellij-rust/blob/c4e9feee4ad46e7953b1948c112533360b6087bb/src/test/kotlin/org/rust/lang/core/macros/RsMacroExpansionTest.kt | ||
66 | // * Port the test to rust and add it to this module | ||
67 | // * Make it pass :-) | ||
68 | |||
69 | #[test] | ||
70 | fn test_token_id_shift() { | ||
71 | let expansion = parse_macro( | ||
72 | r#" | ||
73 | macro_rules! foobar { | ||
74 | ($e:ident) => { foo bar $e } | ||
75 | } | ||
76 | "#, | ||
77 | ) | ||
78 | .expand_tt("foobar!(baz);"); | ||
79 | |||
80 | fn get_id(t: &tt::TokenTree) -> Option<u32> { | ||
81 | if let tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) = t { | ||
82 | return Some(ident.id.0); | ||
83 | } | ||
84 | None | ||
85 | } | ||
86 | |||
87 | assert_eq!(expansion.token_trees.len(), 3); | ||
88 | // {($e:ident) => { foo bar $e }} | ||
89 | // 012345 67 8 9 T 12 | ||
90 | assert_eq!(get_id(&expansion.token_trees[0]), Some(9)); | ||
91 | assert_eq!(get_id(&expansion.token_trees[1]), Some(10)); | ||
92 | |||
93 | // The input args of macro call include parentheses: | ||
94 | // (baz) | ||
95 | // So baz should be 12+1+1 | ||
96 | assert_eq!(get_id(&expansion.token_trees[2]), Some(14)); | ||
97 | } | ||
98 | |||
99 | #[test] | ||
100 | fn test_token_map() { | ||
101 | let expanded = parse_macro( | ||
102 | r#" | ||
103 | macro_rules! foobar { | ||
104 | ($e:ident) => { fn $e() {} } | ||
105 | } | ||
106 | "#, | ||
107 | ) | ||
108 | .expand_tt("foobar!(baz);"); | ||
109 | |||
110 | let (node, token_map) = token_tree_to_syntax_node(&expanded, FragmentKind::Items).unwrap(); | ||
111 | let content = node.syntax_node().to_string(); | ||
112 | |||
113 | let get_text = |id, kind| -> String { | ||
114 | content[token_map.range_by_token(id).unwrap().by_kind(kind).unwrap()].to_string() | ||
115 | }; | ||
116 | |||
117 | assert_eq!(expanded.token_trees.len(), 4); | ||
118 | // {($e:ident) => { fn $e() {} }} | ||
119 | // 012345 67 8 9 T12 3 | ||
120 | |||
121 | assert_eq!(get_text(tt::TokenId(9), IDENT), "fn"); | ||
122 | assert_eq!(get_text(tt::TokenId(12), T!['(']), "("); | ||
123 | assert_eq!(get_text(tt::TokenId(13), T!['{']), "{"); | ||
124 | } | ||
125 | |||
126 | #[test] | ||
127 | fn test_convert_tt() { | ||
128 | parse_macro(r#" | ||
129 | macro_rules! impl_froms { | ||
130 | ($e:ident: $($v:ident),*) => { | ||
131 | $( | ||
132 | impl From<$v> for $e { | ||
133 | fn from(it: $v) -> $e { | ||
134 | $e::$v(it) | ||
135 | } | ||
136 | } | ||
137 | )* | ||
138 | } | ||
139 | } | ||
140 | "#) | ||
141 | .assert_expand_tt( | ||
142 | "impl_froms!(TokenTree: Leaf, Subtree);", | ||
143 | "impl From <Leaf > for TokenTree {fn from (it : Leaf) -> TokenTree {TokenTree ::Leaf (it)}} \ | ||
144 | impl From <Subtree > for TokenTree {fn from (it : Subtree) -> TokenTree {TokenTree ::Subtree (it)}}" | ||
145 | ); | ||
146 | } | ||
147 | |||
148 | #[test] | ||
149 | fn test_convert_tt2() { | ||
150 | parse_macro( | ||
151 | r#" | ||
152 | macro_rules! impl_froms { | ||
153 | ($e:ident: $($v:ident),*) => { | ||
154 | $( | ||
155 | impl From<$v> for $e { | ||
156 | fn from(it: $v) -> $e { | ||
157 | $e::$v(it) | ||
158 | } | ||
159 | } | ||
160 | )* | ||
161 | } | ||
162 | } | ||
163 | "#, | ||
164 | ) | ||
165 | .assert_expand( | ||
166 | "impl_froms!(TokenTree: Leaf, Subtree);", | ||
167 | r#" | ||
168 | SUBTREE $ | ||
169 | IDENT impl 20 | ||
170 | IDENT From 21 | ||
171 | PUNCH < [joint] 22 | ||
172 | IDENT Leaf 53 | ||
173 | PUNCH > [alone] 25 | ||
174 | IDENT for 26 | ||
175 | IDENT TokenTree 51 | ||
176 | SUBTREE {} 29 | ||
177 | IDENT fn 30 | ||
178 | IDENT from 31 | ||
179 | SUBTREE () 32 | ||
180 | IDENT it 33 | ||
181 | PUNCH : [alone] 34 | ||
182 | IDENT Leaf 53 | ||
183 | PUNCH - [joint] 37 | ||
184 | PUNCH > [alone] 38 | ||
185 | IDENT TokenTree 51 | ||
186 | SUBTREE {} 41 | ||
187 | IDENT TokenTree 51 | ||
188 | PUNCH : [joint] 44 | ||
189 | PUNCH : [joint] 45 | ||
190 | IDENT Leaf 53 | ||
191 | SUBTREE () 48 | ||
192 | IDENT it 49 | ||
193 | IDENT impl 20 | ||
194 | IDENT From 21 | ||
195 | PUNCH < [joint] 22 | ||
196 | IDENT Subtree 55 | ||
197 | PUNCH > [alone] 25 | ||
198 | IDENT for 26 | ||
199 | IDENT TokenTree 51 | ||
200 | SUBTREE {} 29 | ||
201 | IDENT fn 30 | ||
202 | IDENT from 31 | ||
203 | SUBTREE () 32 | ||
204 | IDENT it 33 | ||
205 | PUNCH : [alone] 34 | ||
206 | IDENT Subtree 55 | ||
207 | PUNCH - [joint] 37 | ||
208 | PUNCH > [alone] 38 | ||
209 | IDENT TokenTree 51 | ||
210 | SUBTREE {} 41 | ||
211 | IDENT TokenTree 51 | ||
212 | PUNCH : [joint] 44 | ||
213 | PUNCH : [joint] 45 | ||
214 | IDENT Subtree 55 | ||
215 | SUBTREE () 48 | ||
216 | IDENT it 49 | ||
217 | "#, | ||
218 | ); | ||
219 | } | ||
220 | |||
221 | #[test] | ||
222 | fn test_lifetime_split() { | ||
223 | parse_macro( | ||
224 | r#" | ||
225 | macro_rules! foo { | ||
226 | ($($t:tt)*) => { $($t)*} | ||
227 | } | ||
228 | "#, | ||
229 | ) | ||
230 | .assert_expand( | ||
231 | r#"foo!(static bar: &'static str = "hello";);"#, | ||
232 | r#" | ||
233 | SUBTREE $ | ||
234 | IDENT static 17 | ||
235 | IDENT bar 18 | ||
236 | PUNCH : [alone] 19 | ||
237 | PUNCH & [alone] 20 | ||
238 | PUNCH ' [joint] 21 | ||
239 | IDENT static 22 | ||
240 | IDENT str 23 | ||
241 | PUNCH = [alone] 24 | ||
242 | LITERAL "hello" 25 | ||
243 | PUNCH ; [joint] 26 | ||
244 | "#, | ||
245 | ); | ||
246 | } | ||
247 | |||
248 | #[test] | ||
249 | fn test_expr_order() { | ||
250 | let expanded = parse_macro( | ||
251 | r#" | ||
252 | macro_rules! foo { | ||
253 | ($ i:expr) => { | ||
254 | fn bar() { $ i * 2; } | ||
255 | } | ||
256 | } | ||
257 | "#, | ||
258 | ) | ||
259 | .expand_items("foo! { 1 + 1}"); | ||
260 | |||
261 | let dump = format!("{:#?}", expanded); | ||
262 | assert_eq_text!( | ||
263 | r#"[email protected] | ||
264 | [email protected] | ||
265 | [email protected] "fn" | ||
266 | [email protected] | ||
267 | [email protected] "bar" | ||
268 | [email protected] | ||
269 | [email protected] "(" | ||
270 | [email protected] ")" | ||
271 | [email protected] | ||
272 | [email protected] "{" | ||
273 | [email protected] | ||
274 | [email protected] | ||
275 | [email protected] | ||
276 | [email protected] | ||
277 | [email protected] "1" | ||
278 | [email protected] "+" | ||
279 | [email protected] | ||
280 | [email protected] "1" | ||
281 | [email protected] "*" | ||
282 | [email protected] | ||
283 | [email protected] "2" | ||
284 | [email protected] ";" | ||
285 | [email protected] "}""#, | ||
286 | dump.trim() | ||
287 | ); | ||
288 | } | ||
289 | |||
290 | #[test] | ||
291 | fn test_fail_match_pattern_by_first_token() { | ||
292 | parse_macro( | ||
293 | r#" | ||
294 | macro_rules! foo { | ||
295 | ($ i:ident) => ( | ||
296 | mod $ i {} | ||
297 | ); | ||
298 | (= $ i:ident) => ( | ||
299 | fn $ i() {} | ||
300 | ); | ||
301 | (+ $ i:ident) => ( | ||
302 | struct $ i; | ||
303 | ) | ||
304 | } | ||
305 | "#, | ||
306 | ) | ||
307 | .assert_expand_items("foo! { foo }", "mod foo {}") | ||
308 | .assert_expand_items("foo! { = bar }", "fn bar () {}") | ||
309 | .assert_expand_items("foo! { + Baz }", "struct Baz ;"); | ||
310 | } | ||
311 | |||
312 | #[test] | ||
313 | fn test_fail_match_pattern_by_last_token() { | ||
314 | parse_macro( | ||
315 | r#" | ||
316 | macro_rules! foo { | ||
317 | ($ i:ident) => ( | ||
318 | mod $ i {} | ||
319 | ); | ||
320 | ($ i:ident =) => ( | ||
321 | fn $ i() {} | ||
322 | ); | ||
323 | ($ i:ident +) => ( | ||
324 | struct $ i; | ||
325 | ) | ||
326 | } | ||
327 | "#, | ||
328 | ) | ||
329 | .assert_expand_items("foo! { foo }", "mod foo {}") | ||
330 | .assert_expand_items("foo! { bar = }", "fn bar () {}") | ||
331 | .assert_expand_items("foo! { Baz + }", "struct Baz ;"); | ||
332 | } | ||
333 | |||
334 | #[test] | ||
335 | fn test_fail_match_pattern_by_word_token() { | ||
336 | parse_macro( | ||
337 | r#" | ||
338 | macro_rules! foo { | ||
339 | ($ i:ident) => ( | ||
340 | mod $ i {} | ||
341 | ); | ||
342 | (spam $ i:ident) => ( | ||
343 | fn $ i() {} | ||
344 | ); | ||
345 | (eggs $ i:ident) => ( | ||
346 | struct $ i; | ||
347 | ) | ||
348 | } | ||
349 | "#, | ||
350 | ) | ||
351 | .assert_expand_items("foo! { foo }", "mod foo {}") | ||
352 | .assert_expand_items("foo! { spam bar }", "fn bar () {}") | ||
353 | .assert_expand_items("foo! { eggs Baz }", "struct Baz ;"); | ||
354 | } | ||
355 | |||
356 | #[test] | ||
357 | fn test_match_group_pattern_by_separator_token() { | ||
358 | parse_macro( | ||
359 | r#" | ||
360 | macro_rules! foo { | ||
361 | ($ ($ i:ident),*) => ($ ( | ||
362 | mod $ i {} | ||
363 | )*); | ||
364 | ($ ($ i:ident)#*) => ($ ( | ||
365 | fn $ i() {} | ||
366 | )*); | ||
367 | ($ i:ident ,# $ j:ident) => ( | ||
368 | struct $ i; | ||
369 | struct $ j; | ||
370 | ) | ||
371 | } | ||
372 | "#, | ||
373 | ) | ||
374 | .assert_expand_items("foo! { foo, bar }", "mod foo {} mod bar {}") | ||
375 | .assert_expand_items("foo! { foo# bar }", "fn foo () {} fn bar () {}") | ||
376 | .assert_expand_items("foo! { Foo,# Bar }", "struct Foo ; struct Bar ;"); | ||
377 | } | ||
378 | |||
379 | #[test] | ||
380 | fn test_match_group_pattern_with_multiple_defs() { | ||
381 | parse_macro( | ||
382 | r#" | ||
383 | macro_rules! foo { | ||
384 | ($ ($ i:ident),*) => ( struct Bar { $ ( | ||
385 | fn $ i {} | ||
386 | )*} ); | ||
387 | } | ||
388 | "#, | ||
389 | ) | ||
390 | .assert_expand_items("foo! { foo, bar }", "struct Bar {fn foo {} fn bar {}}"); | ||
391 | } | ||
392 | |||
393 | #[test] | ||
394 | fn test_match_group_pattern_with_multiple_statement() { | ||
395 | parse_macro( | ||
396 | r#" | ||
397 | macro_rules! foo { | ||
398 | ($ ($ i:ident),*) => ( fn baz { $ ( | ||
399 | $ i (); | ||
400 | )*} ); | ||
401 | } | ||
402 | "#, | ||
403 | ) | ||
404 | .assert_expand_items("foo! { foo, bar }", "fn baz {foo () ; bar () ;}"); | ||
405 | } | ||
406 | |||
407 | #[test] | ||
408 | fn test_match_group_pattern_with_multiple_statement_without_semi() { | ||
409 | parse_macro( | ||
410 | r#" | ||
411 | macro_rules! foo { | ||
412 | ($ ($ i:ident),*) => ( fn baz { $ ( | ||
413 | $i() | ||
414 | );*} ); | ||
415 | } | ||
416 | "#, | ||
417 | ) | ||
418 | .assert_expand_items("foo! { foo, bar }", "fn baz {foo () ;bar ()}"); | ||
419 | } | ||
420 | |||
421 | #[test] | ||
422 | fn test_match_group_empty_fixed_token() { | ||
423 | parse_macro( | ||
424 | r#" | ||
425 | macro_rules! foo { | ||
426 | ($ ($ i:ident)* #abc) => ( fn baz { $ ( | ||
427 | $ i (); | ||
428 | )*} ); | ||
429 | } | ||
430 | "#, | ||
431 | ) | ||
432 | .assert_expand_items("foo! {#abc}", "fn baz {}"); | ||
433 | } | ||
434 | |||
435 | #[test] | ||
436 | fn test_match_group_in_subtree() { | ||
437 | parse_macro( | ||
438 | r#" | ||
439 | macro_rules! foo { | ||
440 | (fn $name:ident {$($i:ident)*} ) => ( fn $name() { $ ( | ||
441 | $ i (); | ||
442 | )*} ); | ||
443 | }"#, | ||
444 | ) | ||
445 | .assert_expand_items("foo! {fn baz {a b} }", "fn baz () {a () ; b () ;}"); | ||
446 | } | ||
447 | |||
448 | #[test] | ||
449 | fn test_match_group_with_multichar_sep() { | ||
450 | parse_macro( | ||
451 | r#" | ||
452 | macro_rules! foo { | ||
453 | (fn $name:ident {$($i:literal)*} ) => ( fn $name() -> bool { $($i)&&*} ); | ||
454 | }"#, | ||
455 | ) | ||
456 | .assert_expand_items("foo! (fn baz {true true} );", "fn baz () -> bool {true &&true}"); | ||
457 | } | ||
458 | |||
459 | #[test] | ||
460 | fn test_match_group_with_multichar_sep2() { | ||
461 | parse_macro( | ||
462 | r#" | ||
463 | macro_rules! foo { | ||
464 | (fn $name:ident {$($i:literal)&&*} ) => ( fn $name() -> bool { $($i)&&*} ); | ||
465 | }"#, | ||
466 | ) | ||
467 | .assert_expand_items("foo! (fn baz {true && true} );", "fn baz () -> bool {true &&true}"); | ||
468 | } | ||
469 | |||
470 | #[test] | ||
471 | fn test_match_group_zero_match() { | ||
472 | parse_macro( | ||
473 | r#" | ||
474 | macro_rules! foo { | ||
475 | ( $($i:ident)* ) => (); | ||
476 | }"#, | ||
477 | ) | ||
478 | .assert_expand_items("foo! ();", ""); | ||
479 | } | ||
480 | |||
481 | #[test] | ||
482 | fn test_match_group_in_group() { | ||
483 | parse_macro( | ||
484 | r#" | ||
485 | macro_rules! foo { | ||
486 | { $( ( $($i:ident)* ) )* } => ( $( ( $($i)* ) )* ); | ||
487 | }"#, | ||
488 | ) | ||
489 | .assert_expand_items("foo! ( (a b) );", "(a b)"); | ||
490 | } | ||
491 | |||
492 | #[test] | ||
493 | fn test_expand_to_item_list() { | ||
494 | let tree = parse_macro( | ||
495 | " | ||
496 | macro_rules! structs { | ||
497 | ($($i:ident),*) => { | ||
498 | $(struct $i { field: u32 } )* | ||
499 | } | ||
500 | } | ||
501 | ", | ||
502 | ) | ||
503 | .expand_items("structs!(Foo, Bar);"); | ||
504 | assert_eq!( | ||
505 | format!("{:#?}", tree).trim(), | ||
506 | r#" | ||
507 | [email protected] | ||
508 | [email protected] | ||
509 | [email protected] "struct" | ||
510 | [email protected] | ||
511 | [email protected] "Foo" | ||
512 | [email protected] | ||
513 | [email protected] "{" | ||
514 | [email protected] | ||
515 | [email protected] | ||
516 | [email protected] "field" | ||
517 | [email protected] ":" | ||
518 | [email protected] | ||
519 | [email protected] | ||
520 | [email protected] | ||
521 | [email protected] | ||
522 | [email protected] "u32" | ||
523 | [email protected] "}" | ||
524 | [email protected] | ||
525 | [email protected] "struct" | ||
526 | [email protected] | ||
527 | [email protected] "Bar" | ||
528 | [email protected] | ||
529 | [email protected] "{" | ||
530 | [email protected] | ||
531 | [email protected] | ||
532 | [email protected] "field" | ||
533 | [email protected] ":" | ||
534 | [email protected] | ||
535 | [email protected] | ||
536 | [email protected] | ||
537 | [email protected] | ||
538 | [email protected] "u32" | ||
539 | [email protected] "}""# | ||
540 | .trim() | ||
541 | ); | ||
542 | } | ||
543 | |||
544 | fn to_subtree(tt: &tt::TokenTree) -> &tt::Subtree { | ||
545 | if let tt::TokenTree::Subtree(subtree) = tt { | ||
546 | return &subtree; | ||
547 | } | ||
548 | unreachable!("It is not a subtree"); | ||
549 | } | ||
550 | fn to_literal(tt: &tt::TokenTree) -> &tt::Literal { | ||
551 | if let tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) = tt { | ||
552 | return lit; | ||
553 | } | ||
554 | unreachable!("It is not a literal"); | ||
555 | } | ||
556 | |||
557 | fn to_punct(tt: &tt::TokenTree) -> &tt::Punct { | ||
558 | if let tt::TokenTree::Leaf(tt::Leaf::Punct(lit)) = tt { | ||
559 | return lit; | ||
560 | } | ||
561 | unreachable!("It is not a Punct"); | ||
562 | } | ||
563 | |||
564 | #[test] | ||
565 | fn test_expand_literals_to_token_tree() { | ||
566 | let expansion = parse_macro( | ||
567 | r#" | ||
568 | macro_rules! literals { | ||
569 | ($i:ident) => { | ||
570 | { | ||
571 | let a = 'c'; | ||
572 | let c = 1000; | ||
573 | let f = 12E+99_f64; | ||
574 | let s = "rust1"; | ||
575 | } | ||
576 | } | ||
577 | } | ||
578 | "#, | ||
579 | ) | ||
580 | .expand_tt("literals!(foo);"); | ||
581 | let stm_tokens = &to_subtree(&expansion.token_trees[0]).token_trees; | ||
582 | |||
583 | // [let] [a] [=] ['c'] [;] | ||
584 | assert_eq!(to_literal(&stm_tokens[3]).text, "'c'"); | ||
585 | // [let] [c] [=] [1000] [;] | ||
586 | assert_eq!(to_literal(&stm_tokens[5 + 3]).text, "1000"); | ||
587 | // [let] [f] [=] [12E+99_f64] [;] | ||
588 | assert_eq!(to_literal(&stm_tokens[10 + 3]).text, "12E+99_f64"); | ||
589 | // [let] [s] [=] ["rust1"] [;] | ||
590 | assert_eq!(to_literal(&stm_tokens[15 + 3]).text, "\"rust1\""); | ||
591 | } | ||
592 | |||
593 | #[test] | ||
594 | fn test_attr_to_token_tree() { | ||
595 | let expansion = parse_to_token_tree_by_syntax( | ||
596 | r#" | ||
597 | #[derive(Copy)] | ||
598 | struct Foo; | ||
599 | "#, | ||
600 | ); | ||
601 | |||
602 | assert_eq!(to_punct(&expansion.token_trees[0]).char, '#'); | ||
603 | assert_eq!( | ||
604 | to_subtree(&expansion.token_trees[1]).delimiter_kind(), | ||
605 | Some(tt::DelimiterKind::Bracket) | ||
606 | ); | ||
607 | } | ||
608 | |||
609 | #[test] | ||
610 | fn test_two_idents() { | ||
611 | parse_macro( | ||
612 | r#" | ||
613 | macro_rules! foo { | ||
614 | ($ i:ident, $ j:ident) => { | ||
615 | fn foo() { let a = $ i; let b = $j; } | ||
616 | } | ||
617 | } | ||
618 | "#, | ||
619 | ) | ||
620 | .assert_expand_items("foo! { foo, bar }", "fn foo () {let a = foo ; let b = bar ;}"); | ||
621 | } | ||
622 | |||
623 | #[test] | ||
624 | fn test_tt_to_stmts() { | ||
625 | let stmts = parse_macro( | ||
626 | r#" | ||
627 | macro_rules! foo { | ||
628 | () => { | ||
629 | let a = 0; | ||
630 | a = 10 + 1; | ||
631 | a | ||
632 | } | ||
633 | } | ||
634 | "#, | ||
635 | ) | ||
636 | .expand_statements("foo!{}"); | ||
637 | |||
638 | assert_eq!( | ||
639 | format!("{:#?}", stmts).trim(), | ||
640 | r#"[email protected] | ||
641 | [email protected] | ||
642 | [email protected] "let" | ||
643 | [email protected] | ||
644 | [email protected] | ||
645 | [email protected] "a" | ||
646 | [email protected] "=" | ||
647 | [email protected] | ||
648 | [email protected] "0" | ||
649 | [email protected] ";" | ||
650 | [email protected] | ||
651 | [email protected] | ||
652 | [email protected] | ||
653 | [email protected] | ||
654 | [email protected] | ||
655 | [email protected] | ||
656 | [email protected] "a" | ||
657 | [email protected] "=" | ||
658 | [email protected] | ||