diff options
53 files changed, 1043 insertions, 400 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 7bb22c4c4..5f5b7151a 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 { |
@@ -134,6 +134,7 @@ impl ItemTree { | |||
134 | imports, | 134 | imports, |
135 | extern_crates, | 135 | extern_crates, |
136 | functions, | 136 | functions, |
137 | params, | ||
137 | structs, | 138 | structs, |
138 | fields, | 139 | fields, |
139 | unions, | 140 | unions, |
@@ -157,6 +158,7 @@ impl ItemTree { | |||
157 | imports.shrink_to_fit(); | 158 | imports.shrink_to_fit(); |
158 | extern_crates.shrink_to_fit(); | 159 | extern_crates.shrink_to_fit(); |
159 | functions.shrink_to_fit(); | 160 | functions.shrink_to_fit(); |
161 | params.shrink_to_fit(); | ||
160 | structs.shrink_to_fit(); | 162 | structs.shrink_to_fit(); |
161 | fields.shrink_to_fit(); | 163 | fields.shrink_to_fit(); |
162 | unions.shrink_to_fit(); | 164 | unions.shrink_to_fit(); |
@@ -303,6 +305,7 @@ struct ItemTreeData { | |||
303 | imports: Arena<Import>, | 305 | imports: Arena<Import>, |
304 | extern_crates: Arena<ExternCrate>, | 306 | extern_crates: Arena<ExternCrate>, |
305 | functions: Arena<Function>, | 307 | functions: Arena<Function>, |
308 | params: Arena<Param>, | ||
306 | structs: Arena<Struct>, | 309 | structs: Arena<Struct>, |
307 | fields: Arena<Field>, | 310 | fields: Arena<Field>, |
308 | unions: Arena<Union>, | 311 | unions: Arena<Union>, |
@@ -334,6 +337,7 @@ pub enum AttrOwner { | |||
334 | 337 | ||
335 | Variant(Idx<Variant>), | 338 | Variant(Idx<Variant>), |
336 | Field(Idx<Field>), | 339 | Field(Idx<Field>), |
340 | Param(Idx<Param>), | ||
337 | } | 341 | } |
338 | 342 | ||
339 | macro_rules! from_attrs { | 343 | macro_rules! from_attrs { |
@@ -348,7 +352,7 @@ macro_rules! from_attrs { | |||
348 | }; | 352 | }; |
349 | } | 353 | } |
350 | 354 | ||
351 | from_attrs!(ModItem(ModItem), Variant(Idx<Variant>), Field(Idx<Field>)); | 355 | from_attrs!(ModItem(ModItem), Variant(Idx<Variant>), Field(Idx<Field>), Param(Idx<Param>)); |
352 | 356 | ||
353 | /// Trait implemented by all item nodes in the item tree. | 357 | /// Trait implemented by all item nodes in the item tree. |
354 | pub trait ItemTreeNode: Clone { | 358 | pub trait ItemTreeNode: Clone { |
@@ -397,7 +401,47 @@ impl<N: ItemTreeNode> fmt::Debug for FileItemTreeId<N> { | |||
397 | } | 401 | } |
398 | } | 402 | } |
399 | 403 | ||
400 | pub type ItemTreeId<N> = InFile<FileItemTreeId<N>>; | 404 | #[derive(Debug)] |
405 | pub struct ItemTreeId<N: ItemTreeNode> { | ||
406 | file: HirFileId, | ||
407 | pub value: FileItemTreeId<N>, | ||
408 | } | ||
409 | |||
410 | impl<N: ItemTreeNode> ItemTreeId<N> { | ||
411 | pub fn new(file: HirFileId, idx: FileItemTreeId<N>) -> Self { | ||
412 | Self { file, value: idx } | ||
413 | } | ||
414 | |||
415 | pub fn file_id(self) -> HirFileId { | ||
416 | self.file | ||
417 | } | ||
418 | |||
419 | pub fn item_tree(self, db: &dyn DefDatabase) -> Arc<ItemTree> { | ||
420 | db.file_item_tree(self.file) | ||
421 | } | ||
422 | } | ||
423 | |||
424 | impl<N: ItemTreeNode> Copy for ItemTreeId<N> {} | ||
425 | impl<N: ItemTreeNode> Clone for ItemTreeId<N> { | ||
426 | fn clone(&self) -> Self { | ||
427 | *self | ||
428 | } | ||
429 | } | ||
430 | |||
431 | impl<N: ItemTreeNode> PartialEq for ItemTreeId<N> { | ||
432 | fn eq(&self, other: &Self) -> bool { | ||
433 | self.file == other.file && self.value == other.value | ||
434 | } | ||
435 | } | ||
436 | |||
437 | impl<N: ItemTreeNode> Eq for ItemTreeId<N> {} | ||
438 | |||
439 | impl<N: ItemTreeNode> Hash for ItemTreeId<N> { | ||
440 | fn hash<H: Hasher>(&self, state: &mut H) { | ||
441 | self.file.hash(state); | ||
442 | self.value.hash(state); | ||
443 | } | ||
444 | } | ||
401 | 445 | ||
402 | macro_rules! mod_items { | 446 | macro_rules! mod_items { |
403 | ( $( $typ:ident in $fld:ident -> $ast:ty ),+ $(,)? ) => { | 447 | ( $( $typ:ident in $fld:ident -> $ast:ty ),+ $(,)? ) => { |
@@ -484,7 +528,7 @@ macro_rules! impl_index { | |||
484 | }; | 528 | }; |
485 | } | 529 | } |
486 | 530 | ||
487 | impl_index!(fields: Field, variants: Variant); | 531 | impl_index!(fields: Field, variants: Variant, params: Param); |
488 | 532 | ||
489 | impl Index<RawVisibilityId> for ItemTree { | 533 | impl Index<RawVisibilityId> for ItemTree { |
490 | type Output = RawVisibility; | 534 | type Output = RawVisibility; |
@@ -560,12 +604,17 @@ pub struct Function { | |||
560 | /// Whether the function is located in an `extern` block (*not* whether it is an | 604 | /// Whether the function is located in an `extern` block (*not* whether it is an |
561 | /// `extern "abi" fn`). | 605 | /// `extern "abi" fn`). |
562 | pub is_in_extern_block: bool, | 606 | pub is_in_extern_block: bool, |
563 | pub params: Box<[Idx<TypeRef>]>, | 607 | pub params: IdRange<Param>, |
564 | pub is_varargs: bool, | ||
565 | pub ret_type: Idx<TypeRef>, | 608 | pub ret_type: Idx<TypeRef>, |
566 | pub ast_id: FileAstId<ast::Fn>, | 609 | pub ast_id: FileAstId<ast::Fn>, |
567 | } | 610 | } |
568 | 611 | ||
612 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
613 | pub enum Param { | ||
614 | Normal(Idx<TypeRef>), | ||
615 | Varargs, | ||
616 | } | ||
617 | |||
569 | #[derive(Debug, Clone, PartialEq, Eq)] | 618 | #[derive(Debug, Clone, PartialEq, Eq)] |
570 | pub struct FunctionQualifier { | 619 | pub struct FunctionQualifier { |
571 | pub is_default: bool, | 620 | pub is_default: bool, |
@@ -796,6 +845,7 @@ pub struct Variant { | |||
796 | pub fields: Fields, | 845 | pub fields: Fields, |
797 | } | 846 | } |
798 | 847 | ||
848 | /// A range of densely allocated ItemTree IDs. | ||
799 | pub struct IdRange<T> { | 849 | pub struct IdRange<T> { |
800 | range: Range<u32>, | 850 | range: Range<u32>, |
801 | _p: PhantomData<T>, | 851 | _p: PhantomData<T>, |
@@ -814,6 +864,12 @@ impl<T> Iterator for IdRange<T> { | |||
814 | } | 864 | } |
815 | } | 865 | } |
816 | 866 | ||
867 | impl<T> DoubleEndedIterator for IdRange<T> { | ||
868 | fn next_back(&mut self) -> Option<Self::Item> { | ||
869 | self.range.next_back().map(|raw| Idx::from_raw(raw.into())) | ||
870 | } | ||
871 | } | ||
872 | |||
817 | impl<T> fmt::Debug for IdRange<T> { | 873 | impl<T> fmt::Debug for IdRange<T> { |
818 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | 874 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
819 | f.debug_tuple(&format!("IdRange::<{}>", type_name::<T>())).field(&self.range).finish() | 875 | 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 7e91b991d..3f558edd8 100644 --- a/crates/hir_def/src/item_tree/lower.rs +++ b/crates/hir_def/src/item_tree/lower.rs | |||
@@ -333,8 +333,8 @@ impl Ctx { | |||
333 | let visibility = self.lower_visibility(func); | 333 | let visibility = self.lower_visibility(func); |
334 | let name = func.name()?.as_name(); | 334 | let name = func.name()?.as_name(); |
335 | 335 | ||
336 | let mut params = Vec::new(); | ||
337 | let mut has_self_param = false; | 336 | let mut has_self_param = false; |
337 | let start_param = self.next_param_idx(); | ||
338 | if let Some(param_list) = func.param_list() { | 338 | if let Some(param_list) = func.param_list() { |
339 | if let Some(self_param) = param_list.self_param() { | 339 | if let Some(self_param) = param_list.self_param() { |
340 | let self_type = match self_param.ty() { | 340 | let self_type = match self_param.ty() { |
@@ -356,22 +356,25 @@ impl Ctx { | |||
356 | } | 356 | } |
357 | } | 357 | } |
358 | }; | 358 | }; |
359 | params.push(self_type); | 359 | let ty = self.data().type_refs.intern(self_type); |
360 | let idx = self.data().params.alloc(Param::Normal(ty)); | ||
361 | self.add_attrs(idx.into(), RawAttrs::new(&self_param, &self.hygiene)); | ||
360 | has_self_param = true; | 362 | has_self_param = true; |
361 | } | 363 | } |
362 | for param in param_list.params() { | 364 | for param in param_list.params() { |
363 | let type_ref = TypeRef::from_ast_opt(&self.body_ctx, param.ty()); | 365 | let idx = match param.dotdotdot_token() { |
364 | params.push(type_ref); | 366 | Some(_) => self.data().params.alloc(Param::Varargs), |
365 | } | 367 | None => { |
366 | } | 368 | let type_ref = TypeRef::from_ast_opt(&self.body_ctx, param.ty()); |
367 | let params = params.into_iter().map(|param| self.data().type_refs.intern(param)).collect(); | 369 | let ty = self.data().type_refs.intern(type_ref); |
368 | 370 | self.data().params.alloc(Param::Normal(ty)) | |
369 | let mut is_varargs = false; | 371 | } |
370 | if let Some(params) = func.param_list() { | 372 | }; |
371 | if let Some(last) = params.params().last() { | 373 | self.add_attrs(idx.into(), RawAttrs::new(¶m, &self.hygiene)); |
372 | is_varargs = last.dotdotdot_token().is_some(); | ||
373 | } | 374 | } |
374 | } | 375 | } |
376 | let end_param = self.next_param_idx(); | ||
377 | let params = IdRange::new(start_param..end_param); | ||
375 | 378 | ||
376 | let ret_type = match func.ret_type().and_then(|rt| rt.ty()) { | 379 | let ret_type = match func.ret_type().and_then(|rt| rt.ty()) { |
377 | Some(type_ref) => TypeRef::from_ast(&self.body_ctx, type_ref), | 380 | Some(type_ref) => TypeRef::from_ast(&self.body_ctx, type_ref), |
@@ -419,7 +422,6 @@ impl Ctx { | |||
419 | qualifier, | 422 | qualifier, |
420 | is_in_extern_block: false, | 423 | is_in_extern_block: false, |
421 | params, | 424 | params, |
422 | is_varargs, | ||
423 | ret_type, | 425 | ret_type, |
424 | ast_id, | 426 | ast_id, |
425 | }; | 427 | }; |
@@ -682,9 +684,11 @@ impl Ctx { | |||
682 | GenericsOwner::Function(func) => { | 684 | GenericsOwner::Function(func) => { |
683 | generics.fill(&self.body_ctx, sm, node); | 685 | generics.fill(&self.body_ctx, sm, node); |
684 | // lower `impl Trait` in arguments | 686 | // lower `impl Trait` in arguments |
685 | for param in &*func.params { | 687 | for id in func.params.clone() { |
686 | let param = self.data().type_refs.lookup(*param); | 688 | if let Param::Normal(ty) = self.data().params[id] { |
687 | generics.fill_implicit_impl_trait_args(param); | 689 | let ty = self.data().type_refs.lookup(ty); |
690 | generics.fill_implicit_impl_trait_args(ty); | ||
691 | } | ||
688 | } | 692 | } |
689 | } | 693 | } |
690 | GenericsOwner::Struct | 694 | GenericsOwner::Struct |
@@ -769,6 +773,11 @@ impl Ctx { | |||
769 | self.tree.data.as_ref().map_or(0, |data| data.variants.len() as u32), | 773 | self.tree.data.as_ref().map_or(0, |data| data.variants.len() as u32), |
770 | )) | 774 | )) |
771 | } | 775 | } |
776 | fn next_param_idx(&self) -> Idx<Param> { | ||
777 | Idx::from_raw(RawIdx::from( | ||
778 | self.tree.data.as_ref().map_or(0, |data| data.params.len() as u32), | ||
779 | )) | ||
780 | } | ||
772 | } | 781 | } |
773 | 782 | ||
774 | fn desugar_future_path(orig: TypeRef) -> Path { | 783 | 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/parser/src/grammar/params.rs b/crates/parser/src/grammar/params.rs index e313f6fb7..9e2f02d43 100644 --- a/crates/parser/src/grammar/params.rs +++ b/crates/parser/src/grammar/params.rs | |||
@@ -41,22 +41,32 @@ fn list_(p: &mut Parser, flavor: Flavor) { | |||
41 | FnDef | FnTrait | FnPointer => (T!['('], T![')']), | 41 | FnDef | FnTrait | FnPointer => (T!['('], T![')']), |
42 | }; | 42 | }; |
43 | 43 | ||
44 | let m = p.start(); | 44 | let list_marker = p.start(); |
45 | p.bump(bra); | 45 | p.bump(bra); |
46 | 46 | ||
47 | let mut param_marker = None; | ||
47 | if let FnDef = flavor { | 48 | if let FnDef = flavor { |
48 | // test self_param_outer_attr | 49 | // test self_param_outer_attr |
49 | // fn f(#[must_use] self) {} | 50 | // fn f(#[must_use] self) {} |
50 | let m = p.start(); | 51 | let m = p.start(); |
51 | attributes::outer_attrs(p); | 52 | attributes::outer_attrs(p); |
52 | opt_self_param(p, m); | 53 | match opt_self_param(p, m) { |
54 | Ok(()) => {} | ||
55 | Err(m) => param_marker = Some(m), | ||
56 | } | ||
53 | } | 57 | } |
54 | 58 | ||
55 | while !p.at(EOF) && !p.at(ket) { | 59 | while !p.at(EOF) && !p.at(ket) { |
56 | // test param_outer_arg | 60 | // test param_outer_arg |
57 | // fn f(#[attr1] pat: Type) {} | 61 | // fn f(#[attr1] pat: Type) {} |
58 | let m = p.start(); | 62 | let m = match param_marker.take() { |
59 | attributes::outer_attrs(p); | 63 | Some(m) => m, |
64 | None => { | ||
65 | let m = p.start(); | ||
66 | attributes::outer_attrs(p); | ||
67 | m | ||
68 | } | ||
69 | }; | ||
60 | 70 | ||
61 | if !p.at_ts(PARAM_FIRST) { | 71 | if !p.at_ts(PARAM_FIRST) { |
62 | p.error("expected value parameter"); | 72 | p.error("expected value parameter"); |
@@ -72,8 +82,12 @@ fn list_(p: &mut Parser, flavor: Flavor) { | |||
72 | } | 82 | } |
73 | } | 83 | } |
74 | 84 | ||
85 | if let Some(m) = param_marker { | ||
86 | m.abandon(p); | ||
87 | } | ||
88 | |||
75 | p.expect(ket); | 89 | p.expect(ket); |
76 | m.complete(p, PARAM_LIST); | 90 | list_marker.complete(p, PARAM_LIST); |
77 | } | 91 | } |
78 | 92 | ||
79 | const PARAM_FIRST: TokenSet = patterns::PATTERN_FIRST.union(types::TYPE_FIRST); | 93 | const PARAM_FIRST: TokenSet = patterns::PATTERN_FIRST.union(types::TYPE_FIRST); |
@@ -153,7 +167,7 @@ fn variadic_param(p: &mut Parser) -> bool { | |||
153 | // fn d(&'a mut self, x: i32) {} | 167 | // fn d(&'a mut self, x: i32) {} |
154 | // fn e(mut self) {} | 168 | // fn e(mut self) {} |
155 | // } | 169 | // } |
156 | fn opt_self_param(p: &mut Parser, m: Marker) { | 170 | fn opt_self_param(p: &mut Parser, m: Marker) -> Result<(), Marker> { |
157 | if p.at(T![self]) || p.at(T![mut]) && p.nth(1) == T![self] { | 171 | if p.at(T![self]) || p.at(T![mut]) && p.nth(1) == T![self] { |
158 | p.eat(T![mut]); | 172 | p.eat(T![mut]); |
159 | self_as_name(p); | 173 | self_as_name(p); |
@@ -176,7 +190,7 @@ fn opt_self_param(p: &mut Parser, m: Marker) { | |||
176 | | (T![&], LIFETIME_IDENT, T![self], _) | 190 | | (T![&], LIFETIME_IDENT, T![self], _) |
177 | | (T![&], LIFETIME_IDENT, T![mut], T![self]) | 191 | | (T![&], LIFETIME_IDENT, T![mut], T![self]) |
178 | ) { | 192 | ) { |
179 | return m.abandon(p); | 193 | return Err(m); |
180 | } | 194 | } |
181 | p.bump(T![&]); | 195 | p.bump(T![&]); |
182 | if p.at(LIFETIME_IDENT) { | 196 | if p.at(LIFETIME_IDENT) { |
@@ -189,6 +203,7 @@ fn opt_self_param(p: &mut Parser, m: Marker) { | |||
189 | if !p.at(T![')']) { | 203 | if !p.at(T![')']) { |
190 | p.expect(T![,]); | 204 | p.expect(T![,]); |
191 | } | 205 | } |
206 | Ok(()) | ||
192 | } | 207 | } |
193 | 208 | ||
194 | fn self_as_name(p: &mut Parser) { | 209 | fn self_as_name(p: &mut Parser) { |
diff --git a/crates/rust-analyzer/src/semantic_tokens.rs b/crates/rust-analyzer/src/semantic_tokens.rs index be0bea00b..0cb7d12a7 100644 --- a/crates/rust-analyzer/src/semantic_tokens.rs +++ b/crates/rust-analyzer/src/semantic_tokens.rs | |||
@@ -45,15 +45,16 @@ define_semantic_token_types![ | |||
45 | (BRACKET, "bracket"), | 45 | (BRACKET, "bracket"), |
46 | (BUILTIN_TYPE, "builtinType"), | 46 | (BUILTIN_TYPE, "builtinType"), |
47 | (CHAR_LITERAL, "characterLiteral"), | 47 | (CHAR_LITERAL, "characterLiteral"), |
48 | (COMMA, "comma"), | ||
49 | (COLON, "colon"), | 48 | (COLON, "colon"), |
49 | (COMMA, "comma"), | ||
50 | (CONST_PARAMETER, "constParameter"), | ||
50 | (DOT, "dot"), | 51 | (DOT, "dot"), |
51 | (ESCAPE_SEQUENCE, "escapeSequence"), | 52 | (ESCAPE_SEQUENCE, "escapeSequence"), |
52 | (FORMAT_SPECIFIER, "formatSpecifier"), | 53 | (FORMAT_SPECIFIER, "formatSpecifier"), |
53 | (GENERIC, "generic"), | 54 | (GENERIC, "generic"), |
54 | (CONST_PARAMETER, "constParameter"), | 55 | (INTRA_DOC_LINK, "intraDocLink"), |
55 | (LIFETIME, "lifetime"), | ||
56 | (LABEL, "label"), | 56 | (LABEL, "label"), |
57 | (LIFETIME, "lifetime"), | ||
57 | (PARENTHESIS, "parenthesis"), | 58 | (PARENTHESIS, "parenthesis"), |
58 | (PUNCTUATION, "punctuation"), | 59 | (PUNCTUATION, "punctuation"), |
59 | (SELF_KEYWORD, "selfKeyword"), | 60 | (SELF_KEYWORD, "selfKeyword"), |
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index c63fe2915..70501618e 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs | |||
@@ -435,19 +435,20 @@ fn semantic_token_type_and_modifiers( | |||
435 | SymbolKind::Trait => lsp_types::SemanticTokenType::INTERFACE, | 435 | SymbolKind::Trait => lsp_types::SemanticTokenType::INTERFACE, |
436 | SymbolKind::Macro => lsp_types::SemanticTokenType::MACRO, | 436 | SymbolKind::Macro => lsp_types::SemanticTokenType::MACRO, |
437 | }, | 437 | }, |
438 | HlTag::Attribute => semantic_tokens::ATTRIBUTE, | ||
439 | HlTag::BoolLiteral => semantic_tokens::BOOLEAN, | ||
438 | HlTag::BuiltinType => semantic_tokens::BUILTIN_TYPE, | 440 | HlTag::BuiltinType => semantic_tokens::BUILTIN_TYPE, |
439 | HlTag::None => semantic_tokens::GENERIC, | ||
440 | HlTag::ByteLiteral | HlTag::NumericLiteral => lsp_types::SemanticTokenType::NUMBER, | 441 | HlTag::ByteLiteral | HlTag::NumericLiteral => lsp_types::SemanticTokenType::NUMBER, |
441 | HlTag::BoolLiteral => semantic_tokens::BOOLEAN, | ||
442 | HlTag::StringLiteral => lsp_types::SemanticTokenType::STRING, | ||
443 | HlTag::CharLiteral => semantic_tokens::CHAR_LITERAL, | 442 | HlTag::CharLiteral => semantic_tokens::CHAR_LITERAL, |
444 | HlTag::Comment => lsp_types::SemanticTokenType::COMMENT, | 443 | HlTag::Comment => lsp_types::SemanticTokenType::COMMENT, |
445 | HlTag::Attribute => semantic_tokens::ATTRIBUTE, | 444 | HlTag::EscapeSequence => semantic_tokens::ESCAPE_SEQUENCE, |
446 | HlTag::Keyword => lsp_types::SemanticTokenType::KEYWORD, | ||
447 | HlTag::UnresolvedReference => semantic_tokens::UNRESOLVED_REFERENCE, | ||
448 | HlTag::FormatSpecifier => semantic_tokens::FORMAT_SPECIFIER, | 445 | HlTag::FormatSpecifier => semantic_tokens::FORMAT_SPECIFIER, |
446 | HlTag::IntraDocLink => semantic_tokens::INTRA_DOC_LINK, | ||
447 | HlTag::Keyword => lsp_types::SemanticTokenType::KEYWORD, | ||
448 | HlTag::None => semantic_tokens::GENERIC, | ||
449 | HlTag::Operator => lsp_types::SemanticTokenType::OPERATOR, | 449 | HlTag::Operator => lsp_types::SemanticTokenType::OPERATOR, |
450 | HlTag::EscapeSequence => semantic_tokens::ESCAPE_SEQUENCE, | 450 | HlTag::StringLiteral => lsp_types::SemanticTokenType::STRING, |
451 | HlTag::UnresolvedReference => semantic_tokens::UNRESOLVED_REFERENCE, | ||
451 | HlTag::Punctuation(punct) => match punct { | 452 | HlTag::Punctuation(punct) => match punct { |
452 | HlPunct::Bracket => semantic_tokens::BRACKET, | 453 | HlPunct::Bracket => semantic_tokens::BRACKET, |
453 | HlPunct::Brace => semantic_tokens::BRACE, | 454 | HlPunct::Brace => semantic_tokens::BRACE, |
diff --git a/crates/syntax/src/ast.rs b/crates/syntax/src/ast.rs index 19261686c..38e0b04ef 100644 --- a/crates/syntax/src/ast.rs +++ b/crates/syntax/src/ast.rs | |||
@@ -118,7 +118,7 @@ fn test_doc_comment_none() { | |||
118 | .ok() | 118 | .ok() |
119 | .unwrap(); | 119 | .unwrap(); |
120 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); | 120 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); |
121 | assert!(module.doc_comment_text().is_none()); | 121 | assert!(module.doc_comments().doc_comment_text().is_none()); |
122 | } | 122 | } |
123 | 123 | ||
124 | #[test] | 124 | #[test] |
@@ -133,7 +133,7 @@ fn test_outer_doc_comment_of_items() { | |||
133 | .ok() | 133 | .ok() |
134 | .unwrap(); | 134 | .unwrap(); |
135 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); | 135 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); |
136 | assert_eq!("doc", module.doc_comment_text().unwrap()); | 136 | assert_eq!(" doc", module.doc_comments().doc_comment_text().unwrap()); |
137 | } | 137 | } |
138 | 138 | ||
139 | #[test] | 139 | #[test] |
@@ -148,7 +148,7 @@ fn test_inner_doc_comment_of_items() { | |||
148 | .ok() | 148 | .ok() |
149 | .unwrap(); | 149 | .unwrap(); |
150 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); | 150 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); |
151 | assert!(module.doc_comment_text().is_none()); | 151 | assert!(module.doc_comments().doc_comment_text().is_none()); |
152 | } | 152 | } |
153 | 153 | ||
154 | #[test] | 154 | #[test] |
@@ -162,7 +162,7 @@ fn test_doc_comment_of_statics() { | |||
162 | .ok() | 162 | .ok() |
163 | .unwrap(); | 163 | .unwrap(); |
164 | let st = file.syntax().descendants().find_map(Static::cast).unwrap(); | 164 | let st = file.syntax().descendants().find_map(Static::cast).unwrap(); |
165 | assert_eq!("Number of levels", st.doc_comment_text().unwrap()); | 165 | assert_eq!(" Number of levels", st.doc_comments().doc_comment_text().unwrap()); |
166 | } | 166 | } |
167 | 167 | ||
168 | #[test] | 168 | #[test] |
@@ -181,7 +181,10 @@ fn test_doc_comment_preserves_indents() { | |||
181 | .ok() | 181 | .ok() |
182 | .unwrap(); | 182 | .unwrap(); |
183 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); | 183 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); |
184 | assert_eq!("doc1\n```\nfn foo() {\n // ...\n}\n```", module.doc_comment_text().unwrap()); | 184 | assert_eq!( |
185 | " doc1\n ```\n fn foo() {\n // ...\n }\n ```", | ||
186 | module.doc_comments().doc_comment_text().unwrap() | ||
187 | ); | ||
185 | } | 188 | } |
186 | 189 | ||
187 | #[test] | 190 | #[test] |
@@ -198,7 +201,7 @@ fn test_doc_comment_preserves_newlines() { | |||
198 | .ok() | 201 | .ok() |
199 | .unwrap(); | 202 | .unwrap(); |
200 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); | 203 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); |
201 | assert_eq!("this\nis\nmod\nfoo", module.doc_comment_text().unwrap()); | 204 | assert_eq!(" this\n is\n mod\n foo", module.doc_comments().doc_comment_text().unwrap()); |
202 | } | 205 | } |
203 | 206 | ||
204 | #[test] | 207 | #[test] |
@@ -212,7 +215,7 @@ fn test_doc_comment_single_line_block_strips_suffix() { | |||
212 | .ok() | 215 | .ok() |
213 | .unwrap(); | 216 | .unwrap(); |
214 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); | 217 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); |
215 | assert_eq!("this is mod foo", module.doc_comment_text().unwrap()); | 218 | assert_eq!(" this is mod foo", module.doc_comments().doc_comment_text().unwrap()); |
216 | } | 219 | } |
217 | 220 | ||
218 | #[test] | 221 | #[test] |
@@ -226,7 +229,7 @@ fn test_doc_comment_single_line_block_strips_suffix_whitespace() { | |||
226 | .ok() | 229 | .ok() |
227 | .unwrap(); | 230 | .unwrap(); |
228 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); | 231 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); |
229 | assert_eq!("this is mod foo ", module.doc_comment_text().unwrap()); | 232 | assert_eq!(" this is mod foo ", module.doc_comments().doc_comment_text().unwrap()); |
230 | } | 233 | } |
231 | 234 | ||
232 | #[test] | 235 | #[test] |
@@ -245,8 +248,8 @@ fn test_doc_comment_multi_line_block_strips_suffix() { | |||
245 | .unwrap(); | 248 | .unwrap(); |
246 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); | 249 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); |
247 | assert_eq!( | 250 | assert_eq!( |
248 | " this\n is\n mod foo\n ", | 251 | "\n this\n is\n mod foo\n ", |
249 | module.doc_comment_text().unwrap() | 252 | module.doc_comments().doc_comment_text().unwrap() |
250 | ); | 253 | ); |
251 | } | 254 | } |
252 | 255 | ||
@@ -259,8 +262,8 @@ fn test_comments_preserve_trailing_whitespace() { | |||
259 | .unwrap(); | 262 | .unwrap(); |
260 | let def = file.syntax().descendants().find_map(Struct::cast).unwrap(); | 263 | let def = file.syntax().descendants().find_map(Struct::cast).unwrap(); |
261 | assert_eq!( | 264 | assert_eq!( |
262 | "Representation of a Realm. \nIn the specification these are called Realm Records.", | 265 | " Representation of a Realm. \n In the specification these are called Realm Records.", |
263 | def.doc_comment_text().unwrap() | 266 | def.doc_comments().doc_comment_text().unwrap() |
264 | ); | 267 | ); |
265 | } | 268 | } |
266 | 269 | ||
@@ -276,7 +279,7 @@ fn test_four_slash_line_comment() { | |||
276 | .ok() | 279 | .ok() |
277 | .unwrap(); | 280 | .unwrap(); |
278 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); | 281 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); |
279 | assert_eq!("doc comment", module.doc_comment_text().unwrap()); | 282 | assert_eq!(" doc comment", module.doc_comments().doc_comment_text().unwrap()); |
280 | } | 283 | } |
281 | 284 | ||
282 | #[test] | 285 | #[test] |
diff --git a/crates/syntax/src/ast/token_ext.rs b/crates/syntax/src/ast/token_ext.rs index 977eb8181..6c242d126 100644 --- a/crates/syntax/src/ast/token_ext.rs +++ b/crates/syntax/src/ast/token_ext.rs | |||
@@ -33,23 +33,20 @@ impl ast::Comment { | |||
33 | prefix | 33 | prefix |
34 | } | 34 | } |
35 | 35 | ||
36 | /// Returns the textual content of a doc comment block as a single string. | 36 | /// Returns the textual content of a doc comment node as a single string with prefix and suffix |
37 | /// That is, strips leading `///` (+ optional 1 character of whitespace), | 37 | /// removed. |
38 | /// trailing `*/`, trailing whitespace and then joins the lines. | ||
39 | pub fn doc_comment(&self) -> Option<&str> { | 38 | pub fn doc_comment(&self) -> Option<&str> { |
40 | let kind = self.kind(); | 39 | let kind = self.kind(); |
41 | match kind { | 40 | match kind { |
42 | CommentKind { shape, doc: Some(_) } => { | 41 | CommentKind { shape, doc: Some(_) } => { |
43 | let prefix = kind.prefix(); | 42 | let prefix = kind.prefix(); |
44 | let text = &self.text()[prefix.len()..]; | 43 | let text = &self.text()[prefix.len()..]; |
45 | let ws = text.chars().next().filter(|c| c.is_whitespace()); | 44 | let text = if shape == CommentShape::Block { |
46 | let text = ws.map_or(text, |ws| &text[ws.len_utf8()..]); | 45 | text.strip_suffix("*/").unwrap_or(text) |
47 | match shape { | 46 | } else { |
48 | CommentShape::Block if text.ends_with("*/") => { | 47 | text |
49 | Some(&text[..text.len() - "*/".len()]) | 48 | }; |
50 | } | 49 | Some(text) |
51 | _ => Some(text), | ||
52 | } | ||
53 | } | 50 | } |
54 | _ => None, | 51 | _ => None, |
55 | } | 52 | } |
diff --git a/crates/syntax/src/ast/traits.rs b/crates/syntax/src/ast/traits.rs index 13a769d51..ddd213637 100644 --- a/crates/syntax/src/ast/traits.rs +++ b/crates/syntax/src/ast/traits.rs | |||
@@ -1,8 +1,6 @@ | |||
1 | //! Various traits that are implemented by ast nodes. | 1 | //! Various traits that are implemented by ast nodes. |
2 | //! | 2 | //! |
3 | //! The implementations are usually trivial, and live in generated.rs | 3 | //! The implementations are usually trivial, and live in generated.rs |
4 | use itertools::Itertools; | ||
5 | |||
6 | use crate::{ | 4 | use crate::{ |
7 | ast::{self, support, AstChildren, AstNode, AstToken}, | 5 | ast::{self, support, AstChildren, AstNode, AstToken}, |
8 | syntax_node::SyntaxElementChildren, | 6 | syntax_node::SyntaxElementChildren, |
@@ -72,14 +70,10 @@ pub trait AttrsOwner: AstNode { | |||
72 | } | 70 | } |
73 | } | 71 | } |
74 | 72 | ||
75 | pub trait DocCommentsOwner: AstNode { | 73 | pub trait DocCommentsOwner: AttrsOwner { |
76 | fn doc_comments(&self) -> CommentIter { | 74 | fn doc_comments(&self) -> CommentIter { |
77 | CommentIter { iter: self.syntax().children_with_tokens() } | 75 | CommentIter { iter: self.syntax().children_with_tokens() } |
78 | } | 76 | } |
79 | |||
80 | fn doc_comment_text(&self) -> Option<String> { | ||
81 | self.doc_comments().doc_comment_text() | ||
82 | } | ||
83 | } | 77 | } |
84 | 78 | ||
85 | impl CommentIter { | 79 | impl CommentIter { |
@@ -87,12 +81,12 @@ impl CommentIter { | |||
87 | CommentIter { iter: syntax_node.children_with_tokens() } | 81 | CommentIter { iter: syntax_node.children_with_tokens() } |
88 | } | 82 | } |
89 | 83 | ||
90 | /// Returns the textual content of a doc comment block as a single string. | 84 | #[cfg(test)] |
91 | /// That is, strips leading `///` (+ optional 1 character of whitespace), | ||
92 | /// trailing `*/`, trailing whitespace and then joins the lines. | ||
93 | pub fn doc_comment_text(self) -> Option<String> { | 85 | pub fn doc_comment_text(self) -> Option<String> { |
94 | let docs = | 86 | let docs = itertools::Itertools::join( |
95 | self.filter_map(|comment| comment.doc_comment().map(ToOwned::to_owned)).join("\n"); | 87 | &mut self.filter_map(|comment| comment.doc_comment().map(ToOwned::to_owned)), |
88 | "\n", | ||
89 | ); | ||
96 | if docs.is_empty() { | 90 | if docs.is_empty() { |
97 | None | 91 | None |
98 | } else { | 92 | } else { |
diff --git a/crates/syntax/test_data/parser/inline/ok/0139_param_outer_arg.rast b/crates/syntax/test_data/parser/inline/ok/0139_param_outer_arg.rast index 495e4c51b..a84088bf3 100644 --- a/crates/syntax/test_data/parser/inline/ok/0139_param_outer_arg.rast +++ b/crates/syntax/test_data/parser/inline/ok/0139_param_outer_arg.rast | |||
@@ -6,16 +6,16 @@ [email protected] | |||
6 | [email protected] "f" | 6 | [email protected] "f" |
7 | [email protected] | 7 | [email protected] |
8 | [email protected] "(" | 8 | [email protected] "(" |
9 | ATTR@5..13 | 9 | PARAM@5..23 |
10 | POUND@5..6 "#" | 10 | ATTR@5..13 |
11 | L_BRACK@6..7 "[" | 11 | POUND@5..6 "#" |
12 | PATH@7..12 | 12 | L_BRACK@6..7 "[" |
13 | PATH_SEGMENT@7..12 | 13 | [email protected] |
14 | NAME_REF@7..12 | 14 | PATH_SEGMENT@7..12 |
15 | IDENT@7..12 "attr1" | 15 | NAME_REF@7..12 |
16 | R_BRACK@12..13 "]" | 16 | IDENT@7..12 "attr1" |
17 | WHITESPACE@13..14 " " | 17 | R_BRACK@12..13 "]" |
18 | PARAM@14..23 | 18 | WHITESPACE@13..14 " " |
19 | [email protected] | 19 | [email protected] |
20 | [email protected] | 20 | [email protected] |
21 | [email protected] "pat" | 21 | [email protected] "pat" |
diff --git a/crates/syntax/test_data/parser/ok/0051_parameter_attrs.rast b/crates/syntax/test_data/parser/ok/0051_parameter_attrs.rast index e10521d85..88470c41c 100644 --- a/crates/syntax/test_data/parser/ok/0051_parameter_attrs.rast +++ b/crates/syntax/test_data/parser/ok/0051_parameter_attrs.rast | |||
@@ -6,25 +6,25 @@ [email protected] | |||
6 | [email protected] "g1" | 6 | [email protected] "g1" |
7 | [email protected] | 7 | [email protected] |
8 | [email protected] "(" | 8 | [email protected] "(" |
9 | ATTR@6..14 | 9 | PARAM@6..33 |
10 | POUND@6..7 "#" | 10 | ATTR@6..14 |
11 | L_BRACK@7..8 "[" | 11 | POUND@6..7 "#" |
12 | PATH@8..13 | 12 | L_BRACK@7..8 "[" |
13 | PATH_SEGMENT@8..13 | 13 | [email protected] |
14 | NAME_REF@8..13 | 14 | PATH_SEGMENT@8..13 |
15 | IDENT@8..13 "attr1" | 15 | NAME_REF@8..13 |
16 | R_BRACK@13..14 "]" | 16 | IDENT@8..13 "attr1" |
17 | WHITESPACE@14..15 " " | 17 | R_BRACK@13..14 "]" |
18 | ATTR@15..23 | 18 | WHITESPACE@14..15 " " |
19 | POUND@15..16 "#" | 19 | ATTR@15..23 |
20 | L_BRACK@16..17 "[" | 20 | POUND@15..16 "#" |
21 | PATH@17..22 | 21 | L_BRACK@16..17 "[" |
22 | PATH_SEGMENT@17..22 | 22 | [email protected] |
23 | NAME_REF@17..22 | 23 | PATH_SEGMENT@17..22 |
24 | IDENT@17..22 "attr2" | 24 | NAME_REF@17..22 |
25 | R_BRACK@22..23 "]" | 25 | IDENT@17..22 "attr2" |
26 | WHITESPACE@23..24 " " | 26 | R_BRACK@22..23 "]" |
27 | PARAM@24..33 | 27 | WHITESPACE@23..24 " " |
28 | [email protected] | 28 | [email protected] |
29 | [email protected] | 29 | [email protected] |
30 | [email protected] "pat" | 30 | [email protected] "pat" |
@@ -48,16 +48,16 @@ [email protected] | |||
48 | [email protected] "g2" | 48 | [email protected] "g2" |
49 | [email protected] | 49 | [email protected] |
50 | [email protected] "(" | 50 | [email protected] "(" |
51 | ATT[email protected]2 | 51 | PARAM@44..58 |
52 | POUND@44..45 "#" | 52 | ATTR@44..52 |
53 | L_BRACK@45..46 "[" | 53 | POUND@44..45 "#" |
54 | PATH@46..51 | 54 | L_BRACK@45..46 "[" |
55 | PATH_SEGMENT@46..51 | 55 | [email protected] |
56 | NAME_REF@46..51 | 56 | PATH_SEGMENT@46..51 |
57 | IDENT@46..51 "attr1" | 57 | NAME_REF@46..51 |
58 | R_BRACK@51..52 "]" | 58 | IDENT@46..51 "attr1" |
59 | WHITESPACE@52..53 " " | 59 | R_BRACK@51..52 "]" |
60 | PARAM@53..58 | 60 | WHITESPACE@52..53 " " |
61 | [email protected] | 61 | [email protected] |
62 | [email protected] | 62 | [email protected] |
63 | [email protected] "x" | 63 | [email protected] "x" |
@@ -203,16 +203,16 @@ [email protected] | |||
203 | [email protected] "bar" | 203 | [email protected] "bar" |
204 | [email protected] | 204 | [email protected] |
205 | [email protected] "(" | 205 | [email protected] "(" |
206 | ATT[email protected]04 | 206 | PARAM@197..211 |
207 | POUND@197..198 "#" | 207 | ATTR@197..204 |
208 | L_BRACK@198..199 "[" | 208 | POUND@197..198 "#" |
209 | PATH@199..203 | 209 | L_BRACK@198..199 "[" |
210 | PATH_SEGMENT@199..203 | 210 | [email protected] |
211 | NAME_REF@199..203 | 211 | PATH_SEGMENT@199..203 |
212 | IDENT@199..203 "attr" | 212 | NAME_REF@199..203 |
213 | R_BRACK@203..204 "]" | 213 | IDENT@199..203 "attr" |
214 | WHITESPACE@204..205 " " | 214 | R_BRACK@203..204 "]" |
215 | PARAM@205..211 | 215 | WHITESPACE@204..205 " " |
216 | [email protected] | 216 | [email protected] |
217 | [email protected] "_" | 217 | [email protected] "_" |
218 | [email protected] ":" | 218 | [email protected] ":" |