aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/hir/src/attrs.rs5
-rw-r--r--crates/hir/src/db.rs8
-rw-r--r--crates/hir/src/lib.rs2
-rw-r--r--crates/hir/src/semantics.rs3
-rw-r--r--crates/hir/src/semantics/source_to_def.rs6
-rw-r--r--crates/hir_def/src/adt.rs6
-rw-r--r--crates/hir_def/src/attr.rs146
-rw-r--r--crates/hir_def/src/body/lower.rs27
-rw-r--r--crates/hir_def/src/body/tests.rs2
-rw-r--r--crates/hir_def/src/data.rs62
-rw-r--r--crates/hir_def/src/db.rs4
-rw-r--r--crates/hir_def/src/generics.rs14
-rw-r--r--crates/hir_def/src/item_tree.rs68
-rw-r--r--crates/hir_def/src/item_tree/lower.rs41
-rw-r--r--crates/hir_def/src/nameres.rs2
-rw-r--r--crates/hir_def/src/nameres/collector.rs58
-rw-r--r--crates/hir_def/src/nameres/tests.rs19
-rw-r--r--crates/hir_def/src/nameres/tests/diagnostics.rs19
-rw-r--r--crates/hir_def/src/src.rs16
-rw-r--r--crates/hir_expand/src/builtin_macro.rs21
-rw-r--r--crates/hir_ty/src/diagnostics/expr.rs57
-rw-r--r--crates/hir_ty/src/tests/simple.rs19
-rw-r--r--crates/hir_ty/src/tests/traits.rs21
-rw-r--r--crates/ide/src/doc_links.rs13
-rw-r--r--crates/ide/src/goto_definition.rs24
-rw-r--r--crates/ide/src/hover.rs60
-rw-r--r--crates/ide/src/runnables.rs48
-rw-r--r--crates/ide/src/syntax_highlighting.rs2
-rw-r--r--crates/ide/src/syntax_highlighting/html.rs1
-rw-r--r--crates/ide/src/syntax_highlighting/inject.rs261
-rw-r--r--crates/ide/src/syntax_highlighting/tags.rs10
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html1
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html42
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html1
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_injection.html1
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_strings.html1
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html1
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlighting.html1
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/injection.html1
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html1
-rw-r--r--crates/ide/src/syntax_highlighting/tests.rs41
-rw-r--r--crates/ide_completion/src/lib.rs2
-rw-r--r--crates/ide_db/src/apply_change.rs2
-rw-r--r--crates/ide_db/src/call_info.rs6
-rw-r--r--crates/ide_db/src/call_info/tests.rs82
-rw-r--r--crates/parser/src/grammar/params.rs29
-rw-r--r--crates/rust-analyzer/src/semantic_tokens.rs7
-rw-r--r--crates/rust-analyzer/src/to_proto.rs15
-rw-r--r--crates/syntax/src/ast.rs29
-rw-r--r--crates/syntax/src/ast/token_ext.rs19
-rw-r--r--crates/syntax/src/ast/traits.rs18
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0139_param_outer_arg.rast20
-rw-r--r--crates/syntax/test_data/parser/ok/0051_parameter_attrs.rast78
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;
11use syntax::ast; 11use syntax::ast;
12 12
13use crate::{ 13use crate::{
14 Adt, Const, ConstParam, Enum, Field, Function, GenericParam, LifetimeParam, MacroDef, Module, 14 Adt, Const, ConstParam, Enum, Field, Function, GenericParam, Impl, LifetimeParam, MacroDef,
15 ModuleDef, Static, Struct, Trait, TypeAlias, TypeParam, Union, Variant, 15 Module, ModuleDef, Static, Struct, Trait, TypeAlias, TypeParam, Union, Variant,
16}; 16};
17 17
18pub trait HasAttrs { 18pub trait HasAttrs {
@@ -64,6 +64,7 @@ impl_has_attrs![
64 (Adt, AdtId), 64 (Adt, AdtId),
65 (Module, ModuleId), 65 (Module, ModuleId),
66 (GenericParam, GenericParamId), 66 (GenericParam, GenericParamId),
67 (Impl, ImplId),
67]; 68];
68 69
69macro_rules! impl_has_attrs_enum { 70macro_rules! impl_has_attrs_enum {
diff --git a/crates/hir/src/db.rs b/crates/hir/src/db.rs
index d444f4bbb..1902a8d16 100644
--- a/crates/hir/src/db.rs
+++ b/crates/hir/src/db.rs
@@ -3,10 +3,10 @@
3pub use hir_def::db::{ 3pub use hir_def::db::{
4 AttrsQuery, BlockDefMapQuery, BodyQuery, BodyWithSourceMapQuery, ConstDataQuery, 4 AttrsQuery, BlockDefMapQuery, BodyQuery, BodyWithSourceMapQuery, ConstDataQuery,
5 CrateDefMapQueryQuery, CrateLangItemsQuery, DefDatabase, DefDatabaseStorage, EnumDataQuery, 5 CrateDefMapQueryQuery, CrateLangItemsQuery, DefDatabase, DefDatabaseStorage, EnumDataQuery,
6 ExprScopesQuery, FunctionDataQuery, GenericParamsQuery, ImplDataQuery, ImportMapQuery, 6 ExprScopesQuery, FileItemTreeQuery, FunctionDataQuery, GenericParamsQuery, ImplDataQuery,
7 InternConstQuery, InternDatabase, InternDatabaseStorage, InternEnumQuery, InternFunctionQuery, 7 ImportMapQuery, InternConstQuery, InternDatabase, InternDatabaseStorage, InternEnumQuery,
8 InternImplQuery, InternStaticQuery, InternStructQuery, InternTraitQuery, InternTypeAliasQuery, 8 InternFunctionQuery, InternImplQuery, InternStaticQuery, InternStructQuery, InternTraitQuery,
9 InternUnionQuery, ItemTreeQuery, LangItemQuery, StaticDataQuery, StructDataQuery, 9 InternTypeAliasQuery, InternUnionQuery, LangItemQuery, StaticDataQuery, StructDataQuery,
10 TraitDataQuery, TypeAliasDataQuery, UnionDataQuery, 10 TraitDataQuery, TypeAliasDataQuery, UnionDataQuery,
11}; 11};
12pub use hir_expand::db::{ 12pub use hir_expand::db::{
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 861b7329e..b41a36a78 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -89,7 +89,7 @@ pub use crate::{
89pub use { 89pub use {
90 hir_def::{ 90 hir_def::{
91 adt::StructKind, 91 adt::StructKind,
92 attr::{Attrs, Documentation}, 92 attr::{Attr, Attrs, Documentation},
93 body::scope::ExprScopes, 93 body::scope::ExprScopes,
94 find_path::PrefixKind, 94 find_path::PrefixKind,
95 import_map, 95 import_map,
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index 00b076175..15651bb22 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -574,7 +574,7 @@ impl<'db> SemanticsImpl<'db> {
574 } 574 }
575 575
576 fn scope_for_def(&self, def: Trait) -> SemanticsScope<'db> { 576 fn scope_for_def(&self, def: Trait) -> SemanticsScope<'db> {
577 let file_id = self.db.lookup_intern_trait(def.id).id.file_id; 577 let file_id = self.db.lookup_intern_trait(def.id).id.file_id();
578 let resolver = def.id.resolver(self.db.upcast()); 578 let resolver = def.id.resolver(self.db.upcast());
579 SemanticsScope { db: self.db, file_id, resolver } 579 SemanticsScope { db: self.db, file_id, resolver }
580 } 580 }
@@ -752,6 +752,7 @@ macro_rules! to_def_impls {
752 752
753to_def_impls![ 753to_def_impls![
754 (crate::Module, ast::Module, module_to_def), 754 (crate::Module, ast::Module, module_to_def),
755 (crate::Module, ast::SourceFile, source_file_to_def),
755 (crate::Struct, ast::Struct, struct_to_def), 756 (crate::Struct, ast::Struct, struct_to_def),
756 (crate::Enum, ast::Enum, enum_to_def), 757 (crate::Enum, ast::Enum, enum_to_def),
757 (crate::Union, ast::Union, union_to_def), 758 (crate::Union, ast::Union, union_to_def),
diff --git a/crates/hir/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs
index e9d820140..c6ad5ecb5 100644
--- a/crates/hir/src/semantics/source_to_def.rs
+++ b/crates/hir/src/semantics/source_to_def.rs
@@ -71,6 +71,12 @@ impl SourceToDefCtx<'_, '_> {
71 Some(def_map.module_id(child_id)) 71 Some(def_map.module_id(child_id))
72 } 72 }
73 73
74 pub(super) fn source_file_to_def(&mut self, src: InFile<ast::SourceFile>) -> Option<ModuleId> {
75 let _p = profile::span("source_file_to_def");
76 let file_id = src.file_id.original_file(self.db.upcast());
77 self.file_to_def(file_id).get(0).copied()
78 }
79
74 pub(super) fn trait_to_def(&mut self, src: InFile<ast::Trait>) -> Option<TraitId> { 80 pub(super) fn trait_to_def(&mut self, src: InFile<ast::Trait>) -> Option<TraitId> {
75 self.to_def(src, keys::TRAIT) 81 self.to_def(src, keys::TRAIT)
76 } 82 }
diff --git a/crates/hir_def/src/adt.rs b/crates/hir_def/src/adt.rs
index 1b9bb8235..58e35353b 100644
--- a/crates/hir_def/src/adt.rs
+++ b/crates/hir_def/src/adt.rs
@@ -94,7 +94,7 @@ impl StructData {
94 pub(crate) fn struct_data_query(db: &dyn DefDatabase, id: StructId) -> Arc<StructData> { 94 pub(crate) fn struct_data_query(db: &dyn DefDatabase, id: StructId) -> Arc<StructData> {
95 let loc = id.lookup(db); 95 let loc = id.lookup(db);
96 let krate = loc.container.krate; 96 let krate = loc.container.krate;
97 let item_tree = db.item_tree(loc.id.file_id); 97 let item_tree = loc.id.item_tree(db);
98 let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into()); 98 let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into());
99 let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone(); 99 let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone();
100 100
@@ -110,7 +110,7 @@ impl StructData {
110 pub(crate) fn union_data_query(db: &dyn DefDatabase, id: UnionId) -> Arc<StructData> { 110 pub(crate) fn union_data_query(db: &dyn DefDatabase, id: UnionId) -> Arc<StructData> {
111 let loc = id.lookup(db); 111 let loc = id.lookup(db);
112 let krate = loc.container.krate; 112 let krate = loc.container.krate;
113 let item_tree = db.item_tree(loc.id.file_id); 113 let item_tree = loc.id.item_tree(db);
114 let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into()); 114 let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into());
115 let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone(); 115 let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone();
116 116
@@ -130,7 +130,7 @@ impl EnumData {
130 pub(crate) fn enum_data_query(db: &dyn DefDatabase, e: EnumId) -> Arc<EnumData> { 130 pub(crate) fn enum_data_query(db: &dyn DefDatabase, e: EnumId) -> Arc<EnumData> {
131 let loc = e.lookup(db); 131 let loc = e.lookup(db);
132 let krate = loc.container.krate; 132 let krate = loc.container.krate;
133 let item_tree = db.item_tree(loc.id.file_id); 133 let item_tree = loc.id.item_tree(db);
134 let cfg_options = db.crate_graph()[krate].cfg_options.clone(); 134 let cfg_options = db.crate_graph()[krate].cfg_options.clone();
135 135
136 let enum_ = &item_tree[loc.id.value]; 136 let enum_ = &item_tree[loc.id.value];
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs
index b0b4b5052..e4c84afbf 100644
--- a/crates/hir_def/src/attr.rs
+++ b/crates/hir_def/src/attr.rs
@@ -76,37 +76,23 @@ impl ops::Deref for Attrs {
76impl RawAttrs { 76impl RawAttrs {
77 pub(crate) const EMPTY: Self = Self { entries: None }; 77 pub(crate) const EMPTY: Self = Self { entries: None };
78 78
79 pub(crate) fn new(owner: &dyn AttrsOwner, hygiene: &Hygiene) -> Self { 79 pub(crate) fn new(owner: &dyn ast::AttrsOwner, hygiene: &Hygiene) -> Self {
80 let attrs: Vec<_> = collect_attrs(owner).collect(); 80 let entries = collect_attrs(owner)
81 let entries = if attrs.is_empty() { 81 .enumerate()
82 // Avoid heap allocation 82 .flat_map(|(i, attr)| match attr {
83 None 83 Either::Left(attr) => Attr::from_src(attr, hygiene, i as u32),
84 } else { 84 Either::Right(comment) => comment.doc_comment().map(|doc| Attr {
85 Some( 85 index: i as u32,
86 attrs 86 input: Some(AttrInput::Literal(SmolStr::new(doc))),
87 .into_iter() 87 path: ModPath::from(hir_expand::name!(doc)),
88 .enumerate() 88 }),
89 .flat_map(|(i, attr)| match attr { 89 })
90 Either::Left(attr) => Attr::from_src(attr, hygiene).map(|attr| (i, attr)), 90 .collect::<Arc<_>>();
91 Either::Right(comment) => comment.doc_comment().map(|doc| { 91
92 ( 92 Self { entries: if entries.is_empty() { None } else { Some(entries) } }
93 i,
94 Attr {
95 index: 0,
96 input: Some(AttrInput::Literal(SmolStr::new(doc))),
97 path: ModPath::from(hir_expand::name!(doc)),
98 },
99 )
100 }),
101 })
102 .map(|(i, attr)| Attr { index: i as u32, ..attr })
103 .collect(),
104 )
105 };
106 Self { entries }
107 } 93 }
108 94
109 fn from_attrs_owner(db: &dyn DefDatabase, owner: InFile<&dyn AttrsOwner>) -> Self { 95 fn from_attrs_owner(db: &dyn DefDatabase, owner: InFile<&dyn ast::AttrsOwner>) -> Self {
110 let hygiene = Hygiene::new(db.upcast(), owner.file_id); 96 let hygiene = Hygiene::new(db.upcast(), owner.file_id);
111 Self::new(owner.value, &hygiene) 97 Self::new(owner.value, &hygiene)
112 } 98 }
@@ -136,16 +122,15 @@ impl RawAttrs {
136 let new_attrs = self 122 let new_attrs = self
137 .iter() 123 .iter()
138 .flat_map(|attr| -> SmallVec<[_; 1]> { 124 .flat_map(|attr| -> SmallVec<[_; 1]> {
139 let attr = attr.clone();
140 let is_cfg_attr = 125 let is_cfg_attr =
141 attr.path.as_ident().map_or(false, |name| *name == hir_expand::name![cfg_attr]); 126 attr.path.as_ident().map_or(false, |name| *name == hir_expand::name![cfg_attr]);
142 if !is_cfg_attr { 127 if !is_cfg_attr {
143 return smallvec![attr]; 128 return smallvec![attr.clone()];
144 } 129 }
145 130
146 let subtree = match &attr.input { 131 let subtree = match &attr.input {
147 Some(AttrInput::TokenTree(it)) => it, 132 Some(AttrInput::TokenTree(it)) => it,
148 _ => return smallvec![attr], 133 _ => return smallvec![attr.clone()],
149 }; 134 };
150 135
151 // Input subtree is: `(cfg, $(attr),+)` 136 // Input subtree is: `(cfg, $(attr),+)`
@@ -157,11 +142,13 @@ impl RawAttrs {
157 let cfg = parts.next().unwrap(); 142 let cfg = parts.next().unwrap();
158 let cfg = Subtree { delimiter: subtree.delimiter, token_trees: cfg.to_vec() }; 143 let cfg = Subtree { delimiter: subtree.delimiter, token_trees: cfg.to_vec() };
159 let cfg = CfgExpr::parse(&cfg); 144 let cfg = CfgExpr::parse(&cfg);
145 let index = attr.index;
160 let attrs = parts.filter(|a| !a.is_empty()).filter_map(|attr| { 146 let attrs = parts.filter(|a| !a.is_empty()).filter_map(|attr| {
161 let tree = Subtree { delimiter: None, token_trees: attr.to_vec() }; 147 let tree = Subtree { delimiter: None, token_trees: attr.to_vec() };
162 let attr = ast::Attr::parse(&format!("#[{}]", tree)).ok()?; 148 let attr = ast::Attr::parse(&format!("#[{}]", tree)).ok()?;
163 let hygiene = Hygiene::new_unhygienic(); // FIXME 149 // FIXME hygiene
164 Attr::from_src(attr, &hygiene) 150 let hygiene = Hygiene::new_unhygienic();
151 Attr::from_src(attr, &hygiene, index)
165 }); 152 });
166 153
167 let cfg_options = &crate_graph[krate].cfg_options; 154 let cfg_options = &crate_graph[krate].cfg_options;
@@ -191,7 +178,7 @@ impl Attrs {
191 Some(it) => { 178 Some(it) => {
192 let raw_attrs = RawAttrs::from_attrs_owner( 179 let raw_attrs = RawAttrs::from_attrs_owner(
193 db, 180 db,
194 it.as_ref().map(|it| it as &dyn AttrsOwner), 181 it.as_ref().map(|it| it as &dyn ast::AttrsOwner),
195 ); 182 );
196 match mod_data.definition_source(db) { 183 match mod_data.definition_source(db) {
197 InFile { file_id, value: ModuleSource::SourceFile(file) } => raw_attrs 184 InFile { file_id, value: ModuleSource::SourceFile(file) } => raw_attrs
@@ -202,9 +189,9 @@ impl Attrs {
202 None => RawAttrs::from_attrs_owner( 189 None => RawAttrs::from_attrs_owner(
203 db, 190 db,
204 mod_data.definition_source(db).as_ref().map(|src| match src { 191 mod_data.definition_source(db).as_ref().map(|src| match src {
205 ModuleSource::SourceFile(file) => file as &dyn AttrsOwner, 192 ModuleSource::SourceFile(file) => file as &dyn ast::AttrsOwner,
206 ModuleSource::Module(module) => module as &dyn AttrsOwner, 193 ModuleSource::Module(module) => module as &dyn ast::AttrsOwner,
207 ModuleSource::BlockExpr(block) => block as &dyn AttrsOwner, 194 ModuleSource::BlockExpr(block) => block as &dyn ast::AttrsOwner,
208 }), 195 }),
209 ), 196 ),
210 } 197 }
@@ -262,7 +249,7 @@ impl Attrs {
262 let mut res = ArenaMap::default(); 249 let mut res = ArenaMap::default();
263 250
264 for (id, var) in src.value.iter() { 251 for (id, var) in src.value.iter() {
265 let attrs = RawAttrs::from_attrs_owner(db, src.with_value(var as &dyn AttrsOwner)) 252 let attrs = RawAttrs::from_attrs_owner(db, src.with_value(var as &dyn ast::AttrsOwner))
266 .filter(db, krate); 253 .filter(db, krate);
267 254
268 res.insert(id, attrs) 255 res.insert(id, attrs)
@@ -293,6 +280,13 @@ impl Attrs {
293 Arc::new(res) 280 Arc::new(res)
294 } 281 }
295 282
283 /// Constructs a map that maps the lowered `Attr`s in this `Attrs` back to its original syntax nodes.
284 ///
285 /// `owner` must be the original owner of the attributes.
286 pub fn source_map(&self, owner: &dyn ast::AttrsOwner) -> AttrSourceMap {
287 AttrSourceMap { attrs: collect_attrs(owner).collect() }
288 }
289
296 pub fn by_key(&self, key: &'static str) -> AttrQuery<'_> { 290 pub fn by_key(&self, key: &'static str) -> AttrQuery<'_> {
297 AttrQuery { attrs: self, key } 291 AttrQuery { attrs: self, key }
298 } 292 }
@@ -317,15 +311,34 @@ impl Attrs {
317 AttrInput::Literal(s) => Some(s), 311 AttrInput::Literal(s) => Some(s),
318 AttrInput::TokenTree(_) => None, 312 AttrInput::TokenTree(_) => None,
319 }); 313 });
320 // FIXME: Replace `Itertools::intersperse` with `Iterator::intersperse[_with]` until the 314 let indent = docs
321 // libstd api gets stabilized (https://github.com/rust-lang/rust/issues/79524). 315 .clone()
322 let docs = Itertools::intersperse(docs, &SmolStr::new_inline("\n")) 316 .flat_map(|s| s.lines())
323 .map(|it| it.as_str()) 317 .filter(|line| !line.chars().all(|c| c.is_whitespace()))
324 .collect::<String>(); 318 .map(|line| line.chars().take_while(|c| c.is_whitespace()).count())
325 if docs.is_empty() { 319 .min()
320 .unwrap_or(0);
321 let mut buf = String::new();
322 for doc in docs {
323 // str::lines doesn't yield anything for the empty string
324 if !doc.is_empty() {
325 buf.extend(Itertools::intersperse(
326 doc.lines().map(|line| {
327 line.char_indices()
328 .nth(indent)
329 .map_or(line, |(offset, _)| &line[offset..])
330 .trim_end()
331 }),
332 "\n",
333 ));
334 }
335 buf.push('\n');
336 }
337 buf.pop();
338 if buf.is_empty() {
326 None 339 None
327 } else { 340 } else {
328 Some(Documentation(docs)) 341 Some(Documentation(buf))
329 } 342 }
330 } 343 }
331} 344}
@@ -365,6 +378,24 @@ fn inner_attributes(
365 Some((attrs, docs)) 378 Some((attrs, docs))
366} 379}
367 380
381pub struct AttrSourceMap {
382 attrs: Vec<Either<ast::Attr, ast::Comment>>,
383}
384
385impl AttrSourceMap {
386 /// Maps the lowered `Attr` back to its original syntax node.
387 ///
388 /// `attr` must come from the `owner` used for AttrSourceMap
389 ///
390 /// Note that the returned syntax node might be a `#[cfg_attr]`, or a doc comment, instead of
391 /// the attribute represented by `Attr`.
392 pub fn source_of(&self, attr: &Attr) -> &Either<ast::Attr, ast::Comment> {
393 self.attrs
394 .get(attr.index as usize)
395 .unwrap_or_else(|| panic!("cannot find `Attr` at index {}", attr.index))
396 }
397}
398
368#[derive(Debug, Clone, PartialEq, Eq)] 399#[derive(Debug, Clone, PartialEq, Eq)]
369pub struct Attr { 400pub struct Attr {
370 index: u32, 401 index: u32,
@@ -381,7 +412,7 @@ pub enum AttrInput {
381} 412}
382 413
383impl Attr { 414impl Attr {
384 fn from_src(ast: ast::Attr, hygiene: &Hygiene) -> Option<Attr> { 415 fn from_src(ast: ast::Attr, hygiene: &Hygiene, index: u32) -> Option<Attr> {
385 let path = ModPath::from_src(ast.path()?, hygiene)?; 416 let path = ModPath::from_src(ast.path()?, hygiene)?;
386 let input = if let Some(lit) = ast.literal() { 417 let input = if let Some(lit) = ast.literal() {
387 let value = match lit.kind() { 418 let value = match lit.kind() {
@@ -394,7 +425,7 @@ impl Attr {
394 } else { 425 } else {
395 None 426 None
396 }; 427 };
397 Some(Attr { index: 0, path, input }) 428 Some(Attr { index, path, input })
398 } 429 }
399 430
400 /// Maps this lowered `Attr` back to its original syntax node. 431 /// Maps this lowered `Attr` back to its original syntax node.
@@ -403,7 +434,7 @@ impl Attr {
403 /// 434 ///
404 /// Note that the returned syntax node might be a `#[cfg_attr]`, or a doc comment, instead of 435 /// Note that the returned syntax node might be a `#[cfg_attr]`, or a doc comment, instead of
405 /// the attribute represented by `Attr`. 436 /// the attribute represented by `Attr`.
406 pub fn to_src(&self, owner: &dyn AttrsOwner) -> Either<ast::Attr, ast::Comment> { 437 pub fn to_src(&self, owner: &dyn ast::AttrsOwner) -> Either<ast::Attr, ast::Comment> {
407 collect_attrs(owner).nth(self.index as usize).unwrap_or_else(|| { 438 collect_attrs(owner).nth(self.index as usize).unwrap_or_else(|| {
408 panic!("cannot find `Attr` at index {} in {}", self.index, owner.syntax()) 439 panic!("cannot find `Attr` at index {} in {}", self.index, owner.syntax())
409 }) 440 })
@@ -448,6 +479,13 @@ impl Attr {
448 _ => None, 479 _ => None,
449 } 480 }
450 } 481 }
482
483 pub fn string_value(&self) -> Option<&SmolStr> {
484 match self.input.as_ref()? {
485 AttrInput::Literal(it) => Some(it),
486 _ => None,
487 }
488 }
451} 489}
452 490
453#[derive(Debug, Clone, Copy)] 491#[derive(Debug, Clone, Copy)]
@@ -475,7 +513,7 @@ impl<'a> AttrQuery<'a> {
475 self.attrs().next().is_some() 513 self.attrs().next().is_some()
476 } 514 }
477 515
478 pub(crate) fn attrs(self) -> impl Iterator<Item = &'a Attr> { 516 pub fn attrs(self) -> impl Iterator<Item = &'a Attr> + Clone {
479 let key = self.key; 517 let key = self.key;
480 self.attrs 518 self.attrs
481 .iter() 519 .iter()
@@ -488,16 +526,18 @@ where
488 N: ast::AttrsOwner, 526 N: ast::AttrsOwner,
489{ 527{
490 let src = InFile::new(src.file_id, src.to_node(db.upcast())); 528 let src = InFile::new(src.file_id, src.to_node(db.upcast()));
491 RawAttrs::from_attrs_owner(db, src.as_ref().map(|it| it as &dyn AttrsOwner)) 529 RawAttrs::from_attrs_owner(db, src.as_ref().map(|it| it as &dyn ast::AttrsOwner))
492} 530}
493 531
494fn attrs_from_item_tree<N: ItemTreeNode>(id: ItemTreeId<N>, db: &dyn DefDatabase) -> RawAttrs { 532fn attrs_from_item_tree<N: ItemTreeNode>(id: ItemTreeId<N>, db: &dyn DefDatabase) -> RawAttrs {
495 let tree = db.item_tree(id.file_id); 533 let tree = id.item_tree(db);
496 let mod_item = N::id_to_mod_item(id.value); 534 let mod_item = N::id_to_mod_item(id.value);
497 tree.raw_attrs(mod_item.into()).clone() 535 tree.raw_attrs(mod_item.into()).clone()
498} 536}
499 537
500fn collect_attrs(owner: &dyn AttrsOwner) -> impl Iterator<Item = Either<ast::Attr, ast::Comment>> { 538fn collect_attrs(
539 owner: &dyn ast::AttrsOwner,
540) -> impl Iterator<Item = Either<ast::Attr, ast::Comment>> {
501 let (inner_attrs, inner_docs) = inner_attributes(owner.syntax()) 541 let (inner_attrs, inner_docs) = inner_attributes(owner.syntax())
502 .map_or((None, None), |(attrs, docs)| ((Some(attrs), Some(docs)))); 542 .map_or((None, None), |(attrs, docs)| ((Some(attrs), Some(docs))));
503 543
diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs
index 60b25db56..19f5065d1 100644
--- a/crates/hir_def/src/body/lower.rs
+++ b/crates/hir_def/src/body/lower.rs
@@ -177,12 +177,15 @@ impl ExprCollector<'_> {
177 } 177 }
178 178
179 fn collect_expr(&mut self, expr: ast::Expr) -> ExprId { 179 fn collect_expr(&mut self, expr: ast::Expr) -> ExprId {
180 self.maybe_collect_expr(expr).unwrap_or_else(|| self.missing_expr())
181 }
182
183 /// Returns `None` if the expression is `#[cfg]`d out.
184 fn maybe_collect_expr(&mut self, expr: ast::Expr) -> Option<ExprId> {
180 let syntax_ptr = AstPtr::new(&expr); 185 let syntax_ptr = AstPtr::new(&expr);
181 if self.check_cfg(&expr).is_none() { 186 self.check_cfg(&expr)?;
182 return self.missing_expr();
183 }
184 187
185 match expr { 188 Some(match expr {
186 ast::Expr::IfExpr(e) => { 189 ast::Expr::IfExpr(e) => {
187 let then_branch = self.collect_block_opt(e.then_branch()); 190 let then_branch = self.collect_block_opt(e.then_branch());
188 191
@@ -211,8 +214,9 @@ impl ExprCollector<'_> {
211 guard: None, 214 guard: None,
212 }, 215 },
213 ]; 216 ];
214 return self 217 return Some(
215 .alloc_expr(Expr::Match { expr: match_expr, arms }, syntax_ptr); 218 self.alloc_expr(Expr::Match { expr: match_expr, arms }, syntax_ptr),
219 );
216 } 220 }
217 }, 221 },
218 }; 222 };
@@ -283,8 +287,9 @@ impl ExprCollector<'_> {
283 ]; 287 ];
284 let match_expr = 288 let match_expr =
285 self.alloc_expr_desugared(Expr::Match { expr: match_expr, arms }); 289 self.alloc_expr_desugared(Expr::Match { expr: match_expr, arms });
286 return self 290 return Some(
287 .alloc_expr(Expr::Loop { body: match_expr, label }, syntax_ptr); 291 self.alloc_expr(Expr::Loop { body: match_expr, label }, syntax_ptr),
292 );
288 } 293 }
289 }, 294 },
290 }; 295 };
@@ -301,7 +306,7 @@ impl ExprCollector<'_> {
301 ast::Expr::CallExpr(e) => { 306 ast::Expr::CallExpr(e) => {
302 let callee = self.collect_expr_opt(e.expr()); 307 let callee = self.collect_expr_opt(e.expr());
303 let args = if let Some(arg_list) = e.arg_list() { 308 let args = if let Some(arg_list) = e.arg_list() {
304 arg_list.args().map(|e| self.collect_expr(e)).collect() 309 arg_list.args().filter_map(|e| self.maybe_collect_expr(e)).collect()
305 } else { 310 } else {
306 Vec::new() 311 Vec::new()
307 }; 312 };
@@ -310,7 +315,7 @@ impl ExprCollector<'_> {
310 ast::Expr::MethodCallExpr(e) => { 315 ast::Expr::MethodCallExpr(e) => {
311 let receiver = self.collect_expr_opt(e.receiver()); 316 let receiver = self.collect_expr_opt(e.receiver());
312 let args = if let Some(arg_list) = e.arg_list() { 317 let args = if let Some(arg_list) = e.arg_list() {
313 arg_list.args().map(|e| self.collect_expr(e)).collect() 318 arg_list.args().filter_map(|e| self.maybe_collect_expr(e)).collect()
314 } else { 319 } else {
315 Vec::new() 320 Vec::new()
316 }; 321 };
@@ -538,7 +543,7 @@ impl ExprCollector<'_> {
538 self.alloc_expr(Expr::Missing, syntax_ptr) 543 self.alloc_expr(Expr::Missing, syntax_ptr)
539 } 544 }
540 } 545 }
541 } 546 })
542 } 547 }
543 548
544 fn collect_macro_call<F: FnMut(&mut Self, Option<T>), T: ast::AstNode>( 549 fn collect_macro_call<F: FnMut(&mut Self, Option<T>), T: ast::AstNode>(
diff --git a/crates/hir_def/src/body/tests.rs b/crates/hir_def/src/body/tests.rs
index f8e6f70e8..faa133297 100644
--- a/crates/hir_def/src/body/tests.rs
+++ b/crates/hir_def/src/body/tests.rs
@@ -137,7 +137,7 @@ fn f() {
137 include!(invalid); 137 include!(invalid);
138 //^^^^^^^^^^^^^^^^^ could not convert tokens 138 //^^^^^^^^^^^^^^^^^ could not convert tokens
139 include!("does not exist"); 139 include!("does not exist");
140 //^^^^^^^^^^^^^^^^^^^^^^^^^^ could not convert tokens 140 //^^^^^^^^^^^^^^^^^^^^^^^^^^ failed to load file `does not exist`
141 141
142 env!(invalid); 142 env!(invalid);
143 //^^^^^^^^^^^^^ could not convert tokens 143 //^^^^^^^^^^^^^ could not convert tokens
diff --git a/crates/hir_def/src/data.rs b/crates/hir_def/src/data.rs
index 1a27f7bf2..0be868ba2 100644
--- a/crates/hir_def/src/data.rs
+++ b/crates/hir_def/src/data.rs
@@ -9,7 +9,7 @@ use crate::{
9 attr::Attrs, 9 attr::Attrs,
10 body::Expander, 10 body::Expander,
11 db::DefDatabase, 11 db::DefDatabase,
12 item_tree::{AssocItem, FunctionQualifier, ItemTreeId, ModItem}, 12 item_tree::{AssocItem, FunctionQualifier, ItemTreeId, ModItem, Param},
13 type_ref::{TypeBound, TypeRef}, 13 type_ref::{TypeBound, TypeRef},
14 visibility::RawVisibility, 14 visibility::RawVisibility,
15 AssocContainerId, AssocItemId, ConstId, ConstLoc, FunctionId, FunctionLoc, HasModule, ImplId, 15 AssocContainerId, AssocItemId, ConstId, ConstLoc, FunctionId, FunctionLoc, HasModule, ImplId,
@@ -36,19 +36,38 @@ impl FunctionData {
36 pub(crate) fn fn_data_query(db: &dyn DefDatabase, func: FunctionId) -> Arc<FunctionData> { 36 pub(crate) fn fn_data_query(db: &dyn DefDatabase, func: FunctionId) -> Arc<FunctionData> {
37 let loc = func.lookup(db); 37 let loc = func.lookup(db);
38 let krate = loc.container.module(db).krate; 38 let krate = loc.container.module(db).krate;
39 let item_tree = db.item_tree(loc.id.file_id); 39 let crate_graph = db.crate_graph();
40 let cfg_options = &crate_graph[krate].cfg_options;
41 let item_tree = loc.id.item_tree(db);
40 let func = &item_tree[loc.id.value]; 42 let func = &item_tree[loc.id.value];
41 43
44 let enabled_params = func
45 .params
46 .clone()
47 .filter(|&param| item_tree.attrs(db, krate, param.into()).is_cfg_enabled(cfg_options));
48
49 // If last cfg-enabled param is a `...` param, it's a varargs function.
50 let is_varargs = enabled_params
51 .clone()
52 .next_back()
53 .map_or(false, |param| matches!(item_tree[param], Param::Varargs));
54
42 Arc::new(FunctionData { 55 Arc::new(FunctionData {
43 name: func.name.clone(), 56 name: func.name.clone(),
44 params: func.params.iter().map(|id| item_tree[*id].clone()).collect(), 57 params: enabled_params
58 .clone()
59 .filter_map(|id| match &item_tree[id] {
60 Param::Normal(ty) => Some(item_tree[*ty].clone()),
61 Param::Varargs => None,
62 })
63 .collect(),
45 ret_type: item_tree[func.ret_type].clone(), 64 ret_type: item_tree[func.ret_type].clone(),
46 attrs: item_tree.attrs(db, krate, ModItem::from(loc.id.value).into()), 65 attrs: item_tree.attrs(db, krate, ModItem::from(loc.id.value).into()),
47 has_self_param: func.has_self_param, 66 has_self_param: func.has_self_param,
48 has_body: func.has_body, 67 has_body: func.has_body,
49 qualifier: func.qualifier.clone(), 68 qualifier: func.qualifier.clone(),
50 is_in_extern_block: func.is_in_extern_block, 69 is_in_extern_block: func.is_in_extern_block,
51 is_varargs: func.is_varargs, 70 is_varargs,
52 visibility: item_tree[func.visibility].clone(), 71 visibility: item_tree[func.visibility].clone(),
53 }) 72 })
54 } 73 }
@@ -70,7 +89,7 @@ impl TypeAliasData {
70 typ: TypeAliasId, 89 typ: TypeAliasId,
71 ) -> Arc<TypeAliasData> { 90 ) -> Arc<TypeAliasData> {
72 let loc = typ.lookup(db); 91 let loc = typ.lookup(db);
73 let item_tree = db.item_tree(loc.id.file_id); 92 let item_tree = loc.id.item_tree(db);
74 let typ = &item_tree[loc.id.value]; 93 let typ = &item_tree[loc.id.value];
75 94
76 Arc::new(TypeAliasData { 95 Arc::new(TypeAliasData {
@@ -96,23 +115,23 @@ pub struct TraitData {
96impl TraitData { 115impl TraitData {
97 pub(crate) fn trait_data_query(db: &dyn DefDatabase, tr: TraitId) -> Arc<TraitData> { 116 pub(crate) fn trait_data_query(db: &dyn DefDatabase, tr: TraitId) -> Arc<TraitData> {
98 let tr_loc = tr.lookup(db); 117 let tr_loc = tr.lookup(db);
99 let item_tree = db.item_tree(tr_loc.id.file_id); 118 let item_tree = tr_loc.id.item_tree(db);
100 let tr_def = &item_tree[tr_loc.id.value]; 119 let tr_def = &item_tree[tr_loc.id.value];
101 let name = tr_def.name.clone(); 120 let name = tr_def.name.clone();
102 let is_auto = tr_def.is_auto; 121 let is_auto = tr_def.is_auto;
103 let is_unsafe = tr_def.is_unsafe; 122 let is_unsafe = tr_def.is_unsafe;
104 let module_id = tr_loc.container; 123 let module_id = tr_loc.container;
105 let container = AssocContainerId::TraitId(tr); 124 let container = AssocContainerId::TraitId(tr);
106 let mut expander = Expander::new(db, tr_loc.id.file_id, module_id);
107 let visibility = item_tree[tr_def.visibility].clone(); 125 let visibility = item_tree[tr_def.visibility].clone();
108 let bounds = tr_def.bounds.clone(); 126 let bounds = tr_def.bounds.clone();
127 let mut expander = Expander::new(db, tr_loc.id.file_id(), module_id);
109 128
110 let items = collect_items( 129 let items = collect_items(
111 db, 130 db,
112 module_id, 131 module_id,
113 &mut expander, 132 &mut expander,
114 tr_def.items.iter().copied(), 133 tr_def.items.iter().copied(),
115 tr_loc.id.file_id, 134 tr_loc.id.file_id(),
116 container, 135 container,
117 100, 136 100,
118 ); 137 );
@@ -148,21 +167,21 @@ impl ImplData {
148 let _p = profile::span("impl_data_query"); 167 let _p = profile::span("impl_data_query");
149 let impl_loc = id.lookup(db); 168 let impl_loc = id.lookup(db);
150 169
151 let item_tree = db.item_tree(impl_loc.id.file_id); 170 let item_tree = impl_loc.id.item_tree(db);
152 let impl_def = &item_tree[impl_loc.id.value]; 171 let impl_def = &item_tree[impl_loc.id.value];
153 let target_trait = impl_def.target_trait.map(|id| item_tree[id].clone()); 172 let target_trait = impl_def.target_trait.map(|id| item_tree[id].clone());
154 let target_type = item_tree[impl_def.target_type].clone(); 173 let target_type = item_tree[impl_def.target_type].clone();
155 let is_negative = impl_def.is_negative; 174 let is_negative = impl_def.is_negative;
156 let module_id = impl_loc.container; 175 let module_id = impl_loc.container;
157 let container = AssocContainerId::ImplId(id); 176 let container = AssocContainerId::ImplId(id);
158 let mut expander = Expander::new(db, impl_loc.id.file_id, module_id); 177 let mut expander = Expander::new(db, impl_loc.id.file_id(), module_id);
159 178
160 let items = collect_items( 179 let items = collect_items(
161 db, 180 db,
162 module_id, 181 module_id,
163 &mut expander, 182 &mut expander,
164 impl_def.items.iter().copied(), 183 impl_def.items.iter().copied(),
165 impl_loc.id.file_id, 184 impl_loc.id.file_id(),
166 container, 185 container,
167 100, 186 100,
168 ); 187 );
@@ -183,7 +202,7 @@ pub struct ConstData {
183impl ConstData { 202impl ConstData {
184 pub(crate) fn const_data_query(db: &dyn DefDatabase, konst: ConstId) -> Arc<ConstData> { 203 pub(crate) fn const_data_query(db: &dyn DefDatabase, konst: ConstId) -> Arc<ConstData> {
185 let loc = konst.lookup(db); 204 let loc = konst.lookup(db);
186 let item_tree = db.item_tree(loc.id.file_id); 205 let item_tree = loc.id.item_tree(db);
187 let konst = &item_tree[loc.id.value]; 206 let konst = &item_tree[loc.id.value];
188 207
189 Arc::new(ConstData { 208 Arc::new(ConstData {
@@ -206,7 +225,7 @@ pub struct StaticData {
206impl StaticData { 225impl StaticData {
207 pub(crate) fn static_data_query(db: &dyn DefDatabase, konst: StaticId) -> Arc<StaticData> { 226 pub(crate) fn static_data_query(db: &dyn DefDatabase, konst: StaticId) -> Arc<StaticData> {
208 let node = konst.lookup(db); 227 let node = konst.lookup(db);
209 let item_tree = db.item_tree(node.id.file_id); 228 let item_tree = node.id.item_tree(db);
210 let statik = &item_tree[node.id.value]; 229 let statik = &item_tree[node.id.value];
211 230
212 Arc::new(StaticData { 231 Arc::new(StaticData {
@@ -232,22 +251,23 @@ fn collect_items(
232 return Vec::new(); 251 return Vec::new();
233 } 252 }
234 253
235 let item_tree = db.item_tree(file_id); 254 let item_tree = db.file_item_tree(file_id);
236 let cfg_options = db.crate_graph()[module.krate].cfg_options.clone(); 255 let crate_graph = db.crate_graph();
256 let cfg_options = &crate_graph[module.krate].cfg_options;
237 257
238 let mut items = Vec::new(); 258 let mut items = Vec::new();
239 for item in assoc_items { 259 for item in assoc_items {
260 let attrs = item_tree.attrs(db, module.krate, ModItem::from(item).into());
261 if !attrs.is_cfg_enabled(cfg_options) {
262 continue;
263 }
264
240 match item { 265 match item {
241 AssocItem::Function(id) => { 266 AssocItem::Function(id) => {
242 let item = &item_tree[id]; 267 let item = &item_tree[id];
243 let attrs = item_tree.attrs(db, module.krate, ModItem::from(id).into());
244 if !attrs.is_cfg_enabled(&cfg_options) {
245 continue;
246 }
247 let def = FunctionLoc { container, id: ItemTreeId::new(file_id, id) }.intern(db); 268 let def = FunctionLoc { container, id: ItemTreeId::new(file_id, id) }.intern(db);
248 items.push((item.name.clone(), def.into())); 269 items.push((item.name.clone(), def.into()));
249 } 270 }
250 // FIXME: cfg?
251 AssocItem::Const(id) => { 271 AssocItem::Const(id) => {
252 let item = &item_tree[id]; 272 let item = &item_tree[id];
253 let name = match item.name.clone() { 273 let name = match item.name.clone() {
@@ -272,7 +292,7 @@ fn collect_items(
272 if let Ok(res) = res { 292 if let Ok(res) = res {
273 if let Some((mark, mac)) = res.value { 293 if let Some((mark, mac)) = res.value {
274 let src: InFile<ast::MacroItems> = expander.to_source(mac); 294 let src: InFile<ast::MacroItems> = expander.to_source(mac);
275 let item_tree = db.item_tree(src.file_id); 295 let item_tree = db.file_item_tree(src.file_id);
276 let iter = 296 let iter =
277 item_tree.top_level_items().iter().filter_map(ModItem::as_assoc_item); 297 item_tree.top_level_items().iter().filter_map(ModItem::as_assoc_item);
278 items.extend(collect_items( 298 items.extend(collect_items(
diff --git a/crates/hir_def/src/db.rs b/crates/hir_def/src/db.rs
index cca5a086b..276caf5b3 100644
--- a/crates/hir_def/src/db.rs
+++ b/crates/hir_def/src/db.rs
@@ -48,8 +48,8 @@ pub trait InternDatabase: SourceDatabase {
48 48
49#[salsa::query_group(DefDatabaseStorage)] 49#[salsa::query_group(DefDatabaseStorage)]
50pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> { 50pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> {
51 #[salsa::invoke(ItemTree::item_tree_query)] 51 #[salsa::invoke(ItemTree::file_item_tree_query)]
52 fn item_tree(&self, file_id: HirFileId) -> Arc<ItemTree>; 52 fn file_item_tree(&self, file_id: HirFileId) -> Arc<ItemTree>;
53 53
54 #[salsa::invoke(crate_def_map_wait)] 54 #[salsa::invoke(crate_def_map_wait)]
55 #[salsa::transparent] 55 #[salsa::transparent]
diff --git a/crates/hir_def/src/generics.rs b/crates/hir_def/src/generics.rs
index a056ab797..7c6cbff11 100644
--- a/crates/hir_def/src/generics.rs
+++ b/crates/hir_def/src/generics.rs
@@ -97,43 +97,43 @@ impl GenericParams {
97 let generics = match def { 97 let generics = match def {
98 GenericDefId::FunctionId(id) => { 98 GenericDefId::FunctionId(id) => {
99 let id = id.lookup(db).id; 99 let id = id.lookup(db).id;
100 let tree = db.item_tree(id.file_id); 100 let tree = id.item_tree(db);
101 let item = &tree[id.value]; 101 let item = &tree[id.value];
102 tree[item.generic_params].clone() 102 tree[item.generic_params].clone()
103 } 103 }
104 GenericDefId::AdtId(AdtId::StructId(id)) => { 104 GenericDefId::AdtId(AdtId::StructId(id)) => {
105 let id = id.lookup(db).id; 105 let id = id.lookup(db).id;
106 let tree = db.item_tree(id.file_id); 106 let tree = id.item_tree(db);
107 let item = &tree[id.value]; 107 let item = &tree[id.value];
108 tree[item.generic_params].clone() 108 tree[item.generic_params].clone()
109 } 109 }
110 GenericDefId::AdtId(AdtId::EnumId(id)) => { 110 GenericDefId::AdtId(AdtId::EnumId(id)) => {
111 let id = id.lookup(db).id; 111 let id = id.lookup(db).id;
112 let tree = db.item_tree(id.file_id); 112 let tree = id.item_tree(db);
113 let item = &tree[id.value]; 113 let item = &tree[id.value];
114 tree[item.generic_params].clone() 114 tree[item.generic_params].clone()
115 } 115 }
116 GenericDefId::AdtId(AdtId::UnionId(id)) => { 116 GenericDefId::AdtId(AdtId::UnionId(id)) => {
117 let id = id.lookup(db).id; 117 let id = id.lookup(db).id;
118 let tree = db.item_tree(id.file_id); 118 let tree = id.item_tree(db);
119 let item = &tree[id.value]; 119 let item = &tree[id.value];
120 tree[item.generic_params].clone() 120 tree[item.generic_params].clone()
121 } 121 }
122 GenericDefId::TraitId(id) => { 122 GenericDefId::TraitId(id) => {
123 let id = id.lookup(db).id; 123 let id = id.lookup(db).id;
124 let tree = db.item_tree(id.file_id); 124 let tree = id.item_tree(db);
125 let item = &tree[id.value]; 125 let item = &tree[id.value];
126 tree[item.generic_params].clone() 126 tree[item.generic_params].clone()
127 } 127 }
128 GenericDefId::TypeAliasId(id) => { 128 GenericDefId::TypeAliasId(id) => {
129 let id = id.lookup(db).id; 129 let id = id.lookup(db).id;
130 let tree = db.item_tree(id.file_id); 130 let tree = id.item_tree(db);
131 let item = &tree[id.value]; 131 let item = &tree[id.value];
132 tree[item.generic_params].clone() 132 tree[item.generic_params].clone()
133 } 133 }
134 GenericDefId::ImplId(id) => { 134 GenericDefId::ImplId(id) => {
135 let id = id.lookup(db).id; 135 let id = id.lookup(db).id;
136 let tree = db.item_tree(id.file_id); 136 let tree = id.item_tree(db);
137 let item = &tree[id.value]; 137 let item = &tree[id.value];
138 tree[item.generic_params].clone() 138 tree[item.generic_params].clone()
139 } 139 }
diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs
index 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
78impl ItemTree { 78impl ItemTree {
79 pub(crate) fn item_tree_query(db: &dyn DefDatabase, file_id: HirFileId) -> Arc<ItemTree> { 79 pub(crate) fn file_item_tree_query(db: &dyn DefDatabase, file_id: HirFileId) -> Arc<ItemTree> {
80 let _p = profile::span("item_tree_query").detail(|| format!("{:?}", file_id)); 80 let _p = profile::span("item_tree_query").detail(|| format!("{:?}", file_id));
81 let syntax = if let Some(node) = db.parse_or_expand(file_id) { 81 let syntax = if let Some(node) = db.parse_or_expand(file_id) {
82 if node.kind() == SyntaxKind::ERROR { 82 if node.kind() == SyntaxKind::ERROR {
@@ -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
339macro_rules! from_attrs { 343macro_rules! from_attrs {
@@ -348,7 +352,7 @@ macro_rules! from_attrs {
348 }; 352 };
349} 353}
350 354
351from_attrs!(ModItem(ModItem), Variant(Idx<Variant>), Field(Idx<Field>)); 355from_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.
354pub trait ItemTreeNode: Clone { 358pub trait ItemTreeNode: Clone {
@@ -397,7 +401,47 @@ impl<N: ItemTreeNode> fmt::Debug for FileItemTreeId<N> {
397 } 401 }
398} 402}
399 403
400pub type ItemTreeId<N> = InFile<FileItemTreeId<N>>; 404#[derive(Debug)]
405pub struct ItemTreeId<N: ItemTreeNode> {
406 file: HirFileId,
407 pub value: FileItemTreeId<N>,
408}
409
410impl<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
424impl<N: ItemTreeNode> Copy for ItemTreeId<N> {}
425impl<N: ItemTreeNode> Clone for ItemTreeId<N> {
426 fn clone(&self) -> Self {
427 *self
428 }
429}
430
431impl<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
437impl<N: ItemTreeNode> Eq for ItemTreeId<N> {}
438
439impl<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
402macro_rules! mod_items { 446macro_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
487impl_index!(fields: Field, variants: Variant); 531impl_index!(fields: Field, variants: Variant, params: Param);
488 532
489impl Index<RawVisibilityId> for ItemTree { 533impl 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)]
613pub enum Param {
614 Normal(Idx<TypeRef>),
615 Varargs,
616}
617
569#[derive(Debug, Clone, PartialEq, Eq)] 618#[derive(Debug, Clone, PartialEq, Eq)]
570pub struct FunctionQualifier { 619pub 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.
799pub struct IdRange<T> { 849pub 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
867impl<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
817impl<T> fmt::Debug for IdRange<T> { 873impl<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(&param, &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
774fn desugar_future_path(orig: TypeRef) -> Path { 783fn desugar_future_path(orig: TypeRef) -> Path {
diff --git a/crates/hir_def/src/nameres.rs b/crates/hir_def/src/nameres.rs
index 003d668ca..c97be584e 100644
--- a/crates/hir_def/src/nameres.rs
+++ b/crates/hir_def/src/nameres.rs
@@ -213,7 +213,7 @@ impl DefMap {
213 ) -> Option<Arc<DefMap>> { 213 ) -> Option<Arc<DefMap>> {
214 let block: BlockLoc = db.lookup_intern_block(block_id); 214 let block: BlockLoc = db.lookup_intern_block(block_id);
215 215
216 let item_tree = db.item_tree(block.ast_id.file_id); 216 let item_tree = db.file_item_tree(block.ast_id.file_id);
217 if item_tree.inner_items_of_block(block.ast_id.value).is_empty() { 217 if item_tree.inner_items_of_block(block.ast_id.value).is_empty() {
218 return None; 218 return None;
219 } 219 }
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index 81cf652b0..d0fefb5af 100644
--- a/crates/hir_def/src/nameres/collector.rs
+++ b/crates/hir_def/src/nameres/collector.rs
@@ -242,7 +242,7 @@ struct DefCollector<'a> {
242impl DefCollector<'_> { 242impl DefCollector<'_> {
243 fn seed_with_top_level(&mut self) { 243 fn seed_with_top_level(&mut self) {
244 let file_id = self.db.crate_graph()[self.def_map.krate].root_file_id; 244 let file_id = self.db.crate_graph()[self.def_map.krate].root_file_id;
245 let item_tree = self.db.item_tree(file_id.into()); 245 let item_tree = self.db.file_item_tree(file_id.into());
246 let module_id = self.def_map.root; 246 let module_id = self.def_map.root;
247 self.def_map.modules[module_id].origin = ModuleOrigin::CrateRoot { definition: file_id }; 247 self.def_map.modules[module_id].origin = ModuleOrigin::CrateRoot { definition: file_id };
248 if item_tree 248 if item_tree
@@ -263,7 +263,7 @@ impl DefCollector<'_> {
263 } 263 }
264 264
265 fn seed_with_inner(&mut self, block: AstId<ast::BlockExpr>) { 265 fn seed_with_inner(&mut self, block: AstId<ast::BlockExpr>) {
266 let item_tree = self.db.item_tree(block.file_id); 266 let item_tree = self.db.file_item_tree(block.file_id);
267 let module_id = self.def_map.root; 267 let module_id = self.def_map.root;
268 self.def_map.modules[module_id].origin = ModuleOrigin::BlockExpr { block }; 268 self.def_map.modules[module_id].origin = ModuleOrigin::BlockExpr { block };
269 if item_tree 269 if item_tree
@@ -656,26 +656,28 @@ impl DefCollector<'_> {
656 } 656 }
657 } 657 }
658 } else { 658 } else {
659 match import.path.segments().last() { 659 let name = match &import.alias {
660 Some(last_segment) => { 660 Some(ImportAlias::Alias(name)) => Some(name.clone()),
661 let name = match &import.alias { 661 Some(ImportAlias::Underscore) => None,
662 Some(ImportAlias::Alias(name)) => Some(name.clone()), 662 None => match import.path.segments().last() {
663 Some(ImportAlias::Underscore) => None, 663 Some(last_segment) => Some(last_segment.clone()),
664 None => Some(last_segment.clone()), 664 None => {
665 }; 665 cov_mark::hit!(bogus_paths);
666 log::debug!("resolved import {:?} ({:?}) to {:?}", name, import, def); 666 return;
667
668 // extern crates in the crate root are special-cased to insert entries into the extern prelude: rust-lang/rust#54658
669 if import.is_extern_crate && module_id == self.def_map.root {
670 if let (Some(def), Some(name)) = (def.take_types(), name.as_ref()) {
671 self.def_map.extern_prelude.insert(name.clone(), def);
672 }
673 } 667 }
668 },
669 };
670
671 log::debug!("resolved import {:?} ({:?}) to {:?}", name, import, def);
674 672
675 self.update(module_id, &[(name, def)], vis, ImportType::Named); 673 // extern crates in the crate root are special-cased to insert entries into the extern prelude: rust-lang/rust#54658
674 if import.is_extern_crate && module_id == self.def_map.root {
675 if let (Some(def), Some(name)) = (def.take_types(), name.as_ref()) {
676 self.def_map.extern_prelude.insert(name.clone(), def);
676 } 677 }
677 None => cov_mark::hit!(bogus_paths),
678 } 678 }
679
680 self.update(module_id, &[(name, def)], vis, ImportType::Named);
679 } 681 }
680 } 682 }
681 683
@@ -893,7 +895,7 @@ impl DefCollector<'_> {
893 } 895 }
894 896
895 // Then, fetch and process the item tree. This will reuse the expansion result from above. 897 // Then, fetch and process the item tree. This will reuse the expansion result from above.
896 let item_tree = self.db.item_tree(file_id); 898 let item_tree = self.db.file_item_tree(file_id);
897 let mod_dir = self.mod_dirs[&module_id].clone(); 899 let mod_dir = self.mod_dirs[&module_id].clone();
898 ModCollector { 900 ModCollector {
899 def_collector: &mut *self, 901 def_collector: &mut *self,
@@ -949,21 +951,21 @@ impl DefCollector<'_> {
949 let mut diagnosed_extern_crates = FxHashSet::default(); 951 let mut diagnosed_extern_crates = FxHashSet::default();
950 for directive in &self.unresolved_imports { 952 for directive in &self.unresolved_imports {
951 if let ImportSource::ExternCrate(krate) = directive.import.source { 953 if let ImportSource::ExternCrate(krate) = directive.import.source {
952 let item_tree = self.db.item_tree(krate.file_id); 954 let item_tree = krate.item_tree(self.db);
953 let extern_crate = &item_tree[krate.value]; 955 let extern_crate = &item_tree[krate.value];
954 956
955 diagnosed_extern_crates.insert(extern_crate.name.clone()); 957 diagnosed_extern_crates.insert(extern_crate.name.clone());
956 958
957 self.def_map.diagnostics.push(DefDiagnostic::unresolved_extern_crate( 959 self.def_map.diagnostics.push(DefDiagnostic::unresolved_extern_crate(
958 directive.module_id, 960 directive.module_id,
959 InFile::new(krate.file_id, extern_crate.ast_id), 961 InFile::new(krate.file_id(), extern_crate.ast_id),
960 )); 962 ));
961 } 963 }
962 } 964 }
963 965
964 for directive in &self.unresolved_imports { 966 for directive in &self.unresolved_imports {
965 if let ImportSource::Import(import) = &directive.import.source { 967 if let ImportSource::Import(import) = &directive.import.source {
966 let item_tree = self.db.item_tree(import.file_id); 968 let item_tree = import.item_tree(self.db);
967 let import_data = &item_tree[import.value]; 969 let import_data = &item_tree[import.value];
968 970
969 match (import_data.path.segments().first(), &import_data.path.kind) { 971 match (import_data.path.segments().first(), &import_data.path.kind) {
@@ -977,7 +979,7 @@ impl DefCollector<'_> {
977 979
978 self.def_map.diagnostics.push(DefDiagnostic::unresolved_import( 980 self.def_map.diagnostics.push(DefDiagnostic::unresolved_import(
979 directive.module_id, 981 directive.module_id,
980 InFile::new(import.file_id, import_data.ast_id), 982 InFile::new(import.file_id(), import_data.ast_id),
981 import_data.index, 983 import_data.index,
982 )); 984 ));
983 } 985 }
@@ -1053,7 +1055,7 @@ impl ModCollector<'_, '_> {
1053 self.def_collector.db, 1055 self.def_collector.db,
1054 krate, 1056 krate,
1055 &self.item_tree, 1057 &self.item_tree,
1056 InFile::new(self.file_id, import_id), 1058 ItemTreeId::new(self.file_id, import_id),
1057 ), 1059 ),
1058 status: PartialResolvedImport::Unresolved, 1060 status: PartialResolvedImport::Unresolved,
1059 }) 1061 })
@@ -1065,7 +1067,7 @@ impl ModCollector<'_, '_> {
1065 self.def_collector.db, 1067 self.def_collector.db,
1066 krate, 1068 krate,
1067 &self.item_tree, 1069 &self.item_tree,
1068 InFile::new(self.file_id, import_id), 1070 ItemTreeId::new(self.file_id, import_id),
1069 ), 1071 ),
1070 status: PartialResolvedImport::Unresolved, 1072 status: PartialResolvedImport::Unresolved,
1071 }) 1073 })
@@ -1297,7 +1299,7 @@ impl ModCollector<'_, '_> {
1297 Some((file_id, is_mod_rs)), 1299 Some((file_id, is_mod_rs)),
1298 &self.item_tree[module.visibility], 1300 &self.item_tree[module.visibility],
1299 ); 1301 );
1300 let item_tree = db.item_tree(file_id.into()); 1302 let item_tree = db.file_item_tree(file_id.into());
1301 ModCollector { 1303 ModCollector {
1302 def_collector: &mut *self.def_collector, 1304 def_collector: &mut *self.def_collector,
1303 macro_depth: self.macro_depth, 1305 macro_depth: self.macro_depth,
@@ -1469,7 +1471,9 @@ impl ModCollector<'_, '_> {
1469 ) 1471 )
1470 }) 1472 })
1471 }, 1473 },
1472 &mut |err| error = Some(err), 1474 &mut |err| {
1475 error.get_or_insert(err);
1476 },
1473 ) { 1477 ) {
1474 Ok(Ok(macro_call_id)) => { 1478 Ok(Ok(macro_call_id)) => {
1475 self.def_collector.unexpanded_macros.push(MacroDirective { 1479 self.def_collector.unexpanded_macros.push(MacroDirective {
diff --git a/crates/hir_def/src/nameres/tests.rs b/crates/hir_def/src/nameres/tests.rs
index de3aa4f9a..4f2e7a2f9 100644
--- a/crates/hir_def/src/nameres/tests.rs
+++ b/crates/hir_def/src/nameres/tests.rs
@@ -713,3 +713,22 @@ pub fn f() {}
713 "#]], 713 "#]],
714 ); 714 );
715} 715}
716
717#[test]
718fn use_crate_as() {
719 check(
720 r#"
721use crate as foo;
722
723use foo::bar as baz;
724
725fn bar() {}
726 "#,
727 expect![[r#"
728 crate
729 bar: v
730 baz: v
731 foo: t
732 "#]],
733 );
734}
diff --git a/crates/hir_def/src/nameres/tests/diagnostics.rs b/crates/hir_def/src/nameres/tests/diagnostics.rs
index c22ef46fd..a89061c2e 100644
--- a/crates/hir_def/src/nameres/tests/diagnostics.rs
+++ b/crates/hir_def/src/nameres/tests/diagnostics.rs
@@ -196,7 +196,24 @@ fn builtin_macro_fails_expansion() {
196 macro_rules! include { () => {} } 196 macro_rules! include { () => {} }
197 197
198 include!("doesntexist"); 198 include!("doesntexist");
199 //^^^^^^^^^^^^^^^^^^^^^^^^ could not convert tokens 199 //^^^^^^^^^^^^^^^^^^^^^^^^ failed to load file `doesntexist`
200 "#,
201 );
202}
203
204#[test]
205fn good_out_dir_diagnostic() {
206 check_diagnostics(
207 r#"
208 #[rustc_builtin_macro]
209 macro_rules! include { () => {} }
210 #[rustc_builtin_macro]
211 macro_rules! env { () => {} }
212 #[rustc_builtin_macro]
213 macro_rules! concat { () => {} }
214
215 include!(concat!(env!("OUT_DIR"), "/out.rs"));
216 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `OUT_DIR` not set, enable "load out dirs from check" to fix
200 "#, 217 "#,
201 ); 218 );
202} 219}
diff --git a/crates/hir_def/src/src.rs b/crates/hir_def/src/src.rs
index 751d4c052..24e57b469 100644
--- a/crates/hir_def/src/src.rs
+++ b/crates/hir_def/src/src.rs
@@ -14,12 +14,12 @@ impl<N: ItemTreeNode> HasSource for AssocItemLoc<N> {
14 type Value = N::Source; 14 type Value = N::Source;
15 15
16 fn source(&self, db: &dyn DefDatabase) -> InFile<N::Source> { 16 fn source(&self, db: &dyn DefDatabase) -> InFile<N::Source> {
17 let tree = db.item_tree(self.id.file_id); 17 let tree = self.id.item_tree(db);
18 let ast_id_map = db.ast_id_map(self.id.file_id); 18 let ast_id_map = db.ast_id_map(self.id.file_id());
19 let root = db.parse_or_expand(self.id.file_id).unwrap(); 19 let root = db.parse_or_expand(self.id.file_id()).unwrap();
20 let node = &tree[self.id.value]; 20 let node = &tree[self.id.value];
21 21
22 InFile::new(self.id.file_id, ast_id_map.get(node.ast_id()).to_node(&root)) 22 InFile::new(self.id.file_id(), ast_id_map.get(node.ast_id()).to_node(&root))
23 } 23 }
24} 24}
25 25
@@ -27,12 +27,12 @@ impl<N: ItemTreeNode> HasSource for ItemLoc<N> {
27 type Value = N::Source; 27 type Value = N::Source;
28 28
29 fn source(&self, db: &dyn DefDatabase) -> InFile<N::Source> { 29 fn source(&self, db: &dyn DefDatabase) -> InFile<N::Source> {
30 let tree = db.item_tree(self.id.file_id); 30 let tree = self.id.item_tree(db);
31 let ast_id_map = db.ast_id_map(self.id.file_id); 31 let ast_id_map = db.ast_id_map(self.id.file_id());
32 let root = db.parse_or_expand(self.id.file_id).unwrap(); 32 let root = db.parse_or_expand(self.id.file_id()).unwrap();
33 let node = &tree[self.id.value]; 33 let node = &tree[self.id.value];
34 34
35 InFile::new(self.id.file_id, ast_id_map.get(node.ast_id()).to_node(&root)) 35 InFile::new(self.id.file_id(), ast_id_map.get(node.ast_id()).to_node(&root))
36 } 36 }
37} 37}
38 38
diff --git a/crates/hir_expand/src/builtin_macro.rs b/crates/hir_expand/src/builtin_macro.rs
index 2a79c892b..fce09a9e7 100644
--- a/crates/hir_expand/src/builtin_macro.rs
+++ b/crates/hir_expand/src/builtin_macro.rs
@@ -333,17 +333,19 @@ fn concat_expand(
333fn relative_file( 333fn relative_file(
334 db: &dyn AstDatabase, 334 db: &dyn AstDatabase,
335 call_id: MacroCallId, 335 call_id: MacroCallId,
336 path: &str, 336 path_str: &str,
337 allow_recursion: bool, 337 allow_recursion: bool,
338) -> Option<FileId> { 338) -> Result<FileId, mbe::ExpandError> {
339 let call_site = call_id.as_file().original_file(db); 339 let call_site = call_id.as_file().original_file(db);
340 let path = AnchoredPath { anchor: call_site, path }; 340 let path = AnchoredPath { anchor: call_site, path: path_str };
341 let res = db.resolve_path(path)?; 341 let res = db
342 .resolve_path(path)
343 .ok_or_else(|| mbe::ExpandError::Other(format!("failed to load file `{}`", path_str)))?;
342 // Prevent include itself 344 // Prevent include itself
343 if res == call_site && !allow_recursion { 345 if res == call_site && !allow_recursion {
344 None 346 Err(mbe::ExpandError::Other(format!("recursive inclusion of `{}`", path_str)))
345 } else { 347 } else {
346 Some(res) 348 Ok(res)
347 } 349 }
348} 350}
349 351
@@ -364,8 +366,7 @@ fn include_expand(
364) -> ExpandResult<Option<(tt::Subtree, FragmentKind)>> { 366) -> ExpandResult<Option<(tt::Subtree, FragmentKind)>> {
365 let res = (|| { 367 let res = (|| {
366 let path = parse_string(tt)?; 368 let path = parse_string(tt)?;
367 let file_id = relative_file(db, arg_id.into(), &path, false) 369 let file_id = relative_file(db, arg_id.into(), &path, false)?;
368 .ok_or_else(|| mbe::ExpandError::ConversionError)?;
369 370
370 Ok(parse_to_token_tree(&db.file_text(file_id)) 371 Ok(parse_to_token_tree(&db.file_text(file_id))
371 .ok_or_else(|| mbe::ExpandError::ConversionError)? 372 .ok_or_else(|| mbe::ExpandError::ConversionError)?
@@ -417,8 +418,8 @@ fn include_str_expand(
417 // Ideally, we'd be able to offer a precise expansion if the user asks for macro 418 // Ideally, we'd be able to offer a precise expansion if the user asks for macro
418 // expansion. 419 // expansion.
419 let file_id = match relative_file(db, arg_id.into(), &path, true) { 420 let file_id = match relative_file(db, arg_id.into(), &path, true) {
420 Some(file_id) => file_id, 421 Ok(file_id) => file_id,
421 None => { 422 Err(_) => {
422 return ExpandResult::ok(Some((quote!(""), FragmentKind::Expr))); 423 return ExpandResult::ok(Some((quote!(""), FragmentKind::Expr)));
423 } 424 }
424 }; 425 };
diff --git a/crates/hir_ty/src/diagnostics/expr.rs b/crates/hir_ty/src/diagnostics/expr.rs
index 71b2cade0..3909ad354 100644
--- a/crates/hir_ty/src/diagnostics/expr.rs
+++ b/crates/hir_ty/src/diagnostics/expr.rs
@@ -690,4 +690,61 @@ fn main() {
690"#, 690"#,
691 ) 691 )
692 } 692 }
693
694 #[test]
695 fn cfgd_out_call_arguments() {
696 check_diagnostics(
697 r#"
698struct C(#[cfg(FALSE)] ());
699impl C {
700 fn new() -> Self {
701 Self(
702 #[cfg(FALSE)]
703 (),
704 )
705 }
706
707 fn method(&self) {}
708}
709
710fn main() {
711 C::new().method(#[cfg(FALSE)] 0);
712}
713 "#,
714 );
715 }
716
717 #[test]
718 fn cfgd_out_fn_params() {
719 check_diagnostics(
720 r#"
721fn foo(#[cfg(NEVER)] x: ()) {}
722
723struct S;
724
725impl S {
726 fn method(#[cfg(NEVER)] self) {}
727 fn method2(#[cfg(NEVER)] self, arg: u8) {}
728 fn method3(self, #[cfg(NEVER)] arg: u8) {}
729}
730
731extern "C" {
732 fn fixed(fixed: u8, #[cfg(NEVER)] ...);
733 fn varargs(#[cfg(not(NEVER))] ...);
734}
735
736fn main() {
737 foo();
738 S::method();
739 S::method2(0);
740 S::method3(S);
741 S.method3();
742 unsafe {
743 fixed(0);
744 varargs(1, 2, 3);
745 }
746}
747 "#,
748 )
749 }
693} 750}
diff --git a/crates/hir_ty/src/tests/simple.rs b/crates/hir_ty/src/tests/simple.rs
index f5069eba5..bcc43ed70 100644
--- a/crates/hir_ty/src/tests/simple.rs
+++ b/crates/hir_ty/src/tests/simple.rs
@@ -2545,3 +2545,22 @@ fn test() {
2545 "#]], 2545 "#]],
2546 ) 2546 )
2547} 2547}
2548
2549#[test]
2550fn cfgd_out_assoc_items() {
2551 check_types(
2552 r#"
2553struct S;
2554
2555impl S {
2556 #[cfg(FALSE)]
2557 const C: S = S;
2558}
2559
2560fn f() {
2561 S::C;
2562 //^^^^ {unknown}
2563}
2564 "#,
2565 )
2566}
diff --git a/crates/hir_ty/src/tests/traits.rs b/crates/hir_ty/src/tests/traits.rs
index 93d3ad020..8270fa219 100644
--- a/crates/hir_ty/src/tests/traits.rs
+++ b/crates/hir_ty/src/tests/traits.rs
@@ -3253,3 +3253,24 @@ fn f() {
3253 "#, 3253 "#,
3254 ); 3254 );
3255} 3255}
3256
3257#[test]
3258fn nested_inner_function_calling_self() {
3259 check_infer(
3260 r#"
3261struct S;
3262fn f() {
3263 fn inner() -> S {
3264 let s = inner();
3265 }
3266}
3267 "#,
3268 expect![[r#"
3269 17..73 '{ ... } }': ()
3270 39..71 '{ ... }': ()
3271 53..54 's': S
3272 57..62 'inner': fn inner() -> S
3273 57..64 'inner()': S
3274 "#]],
3275 )
3276}
diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs
index 5ea9fc4fb..c7c1f4fee 100644
--- a/crates/ide/src/doc_links.rs
+++ b/crates/ide/src/doc_links.rs
@@ -65,6 +65,8 @@ pub(crate) fn extract_definitions_from_markdown(
65) -> Vec<(String, Option<hir::Namespace>, Range<usize>)> { 65) -> Vec<(String, Option<hir::Namespace>, Range<usize>)> {
66 let mut res = vec![]; 66 let mut res = vec![];
67 let mut cb = |link: BrokenLink| { 67 let mut cb = |link: BrokenLink| {
68 // These allocations are actually unnecessary but the lifetimes on BrokenLinkCallback are wrong
69 // this is fixed in the repo but not on the crates.io release yet
68 Some(( 70 Some((
69 /*url*/ link.reference.to_owned().into(), 71 /*url*/ link.reference.to_owned().into(),
70 /*title*/ link.reference.to_owned().into(), 72 /*title*/ link.reference.to_owned().into(),
@@ -72,13 +74,10 @@ pub(crate) fn extract_definitions_from_markdown(
72 }; 74 };
73 let doc = Parser::new_with_broken_link_callback(markdown, Options::empty(), Some(&mut cb)); 75 let doc = Parser::new_with_broken_link_callback(markdown, Options::empty(), Some(&mut cb));
74 for (event, range) in doc.into_offset_iter() { 76 for (event, range) in doc.into_offset_iter() {
75 match event { 77 if let Event::Start(Tag::Link(_, target, title)) = event {
76 Event::Start(Tag::Link(_link_type, ref target, ref title)) => { 78 let link = if target.is_empty() { title } else { target };
77 let link = if target.is_empty() { title } else { target }; 79 let (link, ns) = parse_link(&link);
78 let (link, ns) = parse_link(link); 80 res.push((link.to_string(), ns, range));
79 res.push((link.to_string(), ns, range));
80 }
81 _ => {}
82 } 81 }
83 } 82 }
84 res 83 res
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs
index b71f4917c..598b47e41 100644
--- a/crates/ide/src/goto_definition.rs
+++ b/crates/ide/src/goto_definition.rs
@@ -1,3 +1,5 @@
1use std::ops::Range;
2
1use either::Either; 3use either::Either;
2use hir::{HasAttrs, ModuleDef, Semantics}; 4use hir::{HasAttrs, ModuleDef, Semantics};
3use ide_db::{ 5use ide_db::{
@@ -5,7 +7,8 @@ use ide_db::{
5 RootDatabase, 7 RootDatabase,
6}; 8};
7use syntax::{ 9use syntax::{
8 ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, TextSize, TokenAtOffset, T, 10 ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, TextRange, TextSize,
11 TokenAtOffset, T,
9}; 12};
10 13
11use crate::{ 14use crate::{
@@ -92,17 +95,18 @@ fn extract_positioned_link_from_comment(
92 position: FilePosition, 95 position: FilePosition,
93 comment: &ast::Comment, 96 comment: &ast::Comment,
94) -> Option<(String, Option<hir::Namespace>)> { 97) -> Option<(String, Option<hir::Namespace>)> {
95 let comment_range = comment.syntax().text_range();
96 let doc_comment = comment.doc_comment()?; 98 let doc_comment = comment.doc_comment()?;
99 let comment_start =
100 comment.syntax().text_range().start() + TextSize::from(comment.prefix().len() as u32);
97 let def_links = extract_definitions_from_markdown(doc_comment); 101 let def_links = extract_definitions_from_markdown(doc_comment);
98 let (def_link, ns, _) = def_links.iter().min_by_key(|(_, _, def_link_range)| { 102 let (def_link, ns, _) = def_links.into_iter().find(|&(_, _, Range { start, end })| {
99 let matched_position = comment_range.start() + TextSize::from(def_link_range.start as u32); 103 TextRange::at(
100 match position.offset.checked_sub(matched_position) { 104 comment_start + TextSize::from(start as u32),
101 Some(distance) => distance, 105 TextSize::from((end - start) as u32),
102 None => comment_range.end(), 106 )
103 } 107 .contains(position.offset)
104 })?; 108 })?;
105 Some((def_link.to_string(), *ns)) 109 Some((def_link, ns))
106} 110}
107 111
108fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> { 112fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
@@ -1136,7 +1140,7 @@ fn foo<'foo>(_: &'foo ()) {
1136 fn goto_def_for_intra_doc_link_same_file() { 1140 fn goto_def_for_intra_doc_link_same_file() {
1137 check( 1141 check(
1138 r#" 1142 r#"
1139/// Blah, [`bar`](bar) .. [`foo`](foo)$0 has [`bar`](bar) 1143/// Blah, [`bar`](bar) .. [`foo`](foo$0) has [`bar`](bar)
1140pub fn bar() { } 1144pub fn bar() { }
1141 1145
1142/// You might want to see [`std::fs::read()`] too. 1146/// You might want to see [`std::fs::read()`] too.
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index 325014622..15d309d7d 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -1533,12 +1533,21 @@ fn my() {}
1533 fn test_hover_struct_doc_comment() { 1533 fn test_hover_struct_doc_comment() {
1534 check( 1534 check(
1535 r#" 1535 r#"
1536/// bar docs 1536/// This is an example
1537/// multiline doc
1538///
1539/// # Example
1540///
1541/// ```
1542/// let five = 5;
1543///
1544/// assert_eq!(6, my_crate::add_one(5));
1545/// ```
1537struct Bar; 1546struct Bar;
1538 1547
1539fn foo() { let bar = Ba$0r; } 1548fn foo() { let bar = Ba$0r; }
1540"#, 1549"#,
1541 expect![[r#" 1550 expect![[r##"
1542 *Bar* 1551 *Bar*
1543 1552
1544 ```rust 1553 ```rust
@@ -1551,8 +1560,17 @@ fn foo() { let bar = Ba$0r; }
1551 1560
1552 --- 1561 ---
1553 1562
1554 bar docs 1563 This is an example
1555 "#]], 1564 multiline doc
1565
1566 # Example
1567
1568 ```
1569 let five = 5;
1570
1571 assert_eq!(6, my_crate::add_one(5));
1572 ```
1573 "##]],
1556 ); 1574 );
1557 } 1575 }
1558 1576
@@ -3424,6 +3442,40 @@ mod Foo$0 {
3424 } 3442 }
3425 3443
3426 #[test] 3444 #[test]
3445 fn hover_doc_block_style_indentend() {
3446 check(
3447 r#"
3448/**
3449 foo
3450 ```rust
3451 let x = 3;
3452 ```
3453*/
3454fn foo$0() {}
3455"#,
3456 expect![[r#"
3457 *foo*
3458
3459 ```rust
3460 test
3461 ```
3462
3463 ```rust
3464 fn foo()
3465 ```
3466
3467 ---
3468
3469 foo
3470
3471 ```rust
3472 let x = 3;
3473 ```
3474 "#]],
3475 );
3476 }
3477
3478 #[test]
3427 fn hover_comments_dont_highlight_parent() { 3479 fn hover_comments_dont_highlight_parent() {
3428 check_hover_no_result( 3480 check_hover_no_result(
3429 r#" 3481 r#"
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs
index 397e2126b..bea020b06 100644
--- a/crates/ide/src/runnables.rs
+++ b/crates/ide/src/runnables.rs
@@ -576,6 +576,20 @@ fn should_have_runnable_1() {}
576/// ``` 576/// ```
577fn should_have_runnable_2() {} 577fn should_have_runnable_2() {}
578 578
579/**
580```rust
581let z = 55;
582```
583*/
584fn should_have_no_runnable_3() {}
585
586/**
587 ```rust
588 let z = 55;
589 ```
590*/
591fn should_have_no_runnable_4() {}
592
579/// ```no_run 593/// ```no_run
580/// let z = 55; 594/// let z = 55;
581/// ``` 595/// ```
@@ -616,7 +630,7 @@ fn should_have_no_runnable_6() {}
616struct StructWithRunnable(String); 630struct StructWithRunnable(String);
617 631
618"#, 632"#,
619 &[&BIN, &DOCTEST, &DOCTEST, &DOCTEST, &DOCTEST], 633 &[&BIN, &DOCTEST, &DOCTEST, &DOCTEST, &DOCTEST, &DOCTEST, &DOCTEST],
620 expect![[r#" 634 expect![[r#"
621 [ 635 [
622 Runnable { 636 Runnable {
@@ -682,7 +696,37 @@ struct StructWithRunnable(String);
682 file_id: FileId( 696 file_id: FileId(
683 0, 697 0,
684 ), 698 ),
685 full_range: 756..821, 699 full_range: 256..320,
700 name: "should_have_no_runnable_3",
701 },
702 kind: DocTest {
703 test_id: Path(
704 "should_have_no_runnable_3",
705 ),
706 },
707 cfg: None,
708 },
709 Runnable {
710 nav: NavigationTarget {
711 file_id: FileId(
712 0,
713 ),
714 full_range: 322..398,
715 name: "should_have_no_runnable_4",
716 },
717 kind: DocTest {
718 test_id: Path(
719 "should_have_no_runnable_4",
720 ),
721 },
722 cfg: None,
723 },
724 Runnable {
725 nav: NavigationTarget {
726 file_id: FileId(
727 0,
728 ),
729 full_range: 900..965,
686 name: "StructWithRunnable", 730 name: "StructWithRunnable",
687 }, 731 },
688 kind: DocTest { 732 kind: DocTest {
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs
index 870146d24..ba3447b3a 100644
--- a/crates/ide/src/syntax_highlighting.rs
+++ b/crates/ide/src/syntax_highlighting.rs
@@ -150,7 +150,7 @@ fn traverse(
150 WalkEvent::Enter(it) => it, 150 WalkEvent::Enter(it) => it,
151 WalkEvent::Leave(it) => { 151 WalkEvent::Leave(it) => {
152 if let Some(node) = it.as_node() { 152 if let Some(node) = it.as_node() {
153 inject::doc_comment(hl, node); 153 inject::doc_comment(hl, sema, node);
154 } 154 }
155 continue; 155 continue;
156 } 156 }
diff --git a/crates/ide/src/syntax_highlighting/html.rs b/crates/ide/src/syntax_highlighting/html.rs
index 0ee7bc96e..1d34731ab 100644
--- a/crates/ide/src/syntax_highlighting/html.rs
+++ b/crates/ide/src/syntax_highlighting/html.rs
@@ -59,6 +59,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
59.label { color: #DFAF8F; font-style: italic; } 59.label { color: #DFAF8F; font-style: italic; }
60.comment { color: #7F9F7F; } 60.comment { color: #7F9F7F; }
61.documentation { color: #629755; } 61.documentation { color: #629755; }
62.intra_doc_link { color: #A9C577; }
62.injected { opacity: 0.65 ; } 63.injected { opacity: 0.65 ; }
63.struct, .enum { color: #7CB8BB; } 64.struct, .enum { color: #7CB8BB; }
64.enum_variant { color: #BDE0F3; } 65.enum_variant { color: #BDE0F3; }
diff --git a/crates/ide/src/syntax_highlighting/inject.rs b/crates/ide/src/syntax_highlighting/inject.rs
index 4f825523c..947cc974c 100644
--- a/crates/ide/src/syntax_highlighting/inject.rs
+++ b/crates/ide/src/syntax_highlighting/inject.rs
@@ -1,10 +1,18 @@
1//! "Recursive" Syntax highlighting for code in doctests and fixtures. 1//! "Recursive" Syntax highlighting for code in doctests and fixtures.
2 2
3use hir::Semantics; 3use std::{mem, ops::Range};
4use ide_db::call_info::ActiveParameter;
5use syntax::{ast, AstToken, SyntaxNode, SyntaxToken, TextRange, TextSize};
6 4
7use crate::{Analysis, HlMod, HlRange, HlTag, RootDatabase}; 5use either::Either;
6use hir::{HasAttrs, Semantics};
7use ide_db::{call_info::ActiveParameter, defs::Definition};
8use syntax::{
9 ast::{self, AstNode, AttrsOwner, DocCommentsOwner},
10 match_ast, AstToken, NodeOrToken, SyntaxNode, SyntaxToken, TextRange, TextSize,
11};
12
13use crate::{
14 doc_links::extract_definitions_from_markdown, Analysis, HlMod, HlRange, HlTag, RootDatabase,
15};
8 16
9use super::{highlights::Highlights, injector::Injector}; 17use super::{highlights::Highlights, injector::Injector};
10 18
@@ -81,70 +89,181 @@ const RUSTDOC_FENCE_TOKENS: &[&'static str] = &[
81 "edition2021", 89 "edition2021",
82]; 90];
83 91
84/// Injection of syntax highlighting of doctests. 92// Basically an owned dyn AttrsOwner without extra Boxing
85pub(super) fn doc_comment(hl: &mut Highlights, node: &SyntaxNode) { 93struct AttrsOwnerNode {
86 let doc_comments = node 94 node: SyntaxNode,
87 .children_with_tokens() 95}
88 .filter_map(|it| it.into_token().and_then(ast::Comment::cast)) 96
89 .filter(|it| it.kind().doc.is_some()); 97impl AttrsOwnerNode {
98 fn new<N: DocCommentsOwner>(node: N) -> Self {
99 AttrsOwnerNode { node: node.syntax().clone() }
100 }
101}
102
103impl AttrsOwner for AttrsOwnerNode {}
104impl AstNode for AttrsOwnerNode {
105 fn can_cast(_: syntax::SyntaxKind) -> bool
106 where
107 Self: Sized,
108 {
109 false
110 }
111 fn cast(_: SyntaxNode) -> Option<Self>
112 where
113 Self: Sized,
114 {
115 None
116 }
117 fn syntax(&self) -> &SyntaxNode {
118 &self.node
119 }
120}
90 121
91 if !doc_comments.clone().any(|it| it.text().contains(RUSTDOC_FENCE)) { 122fn doc_attributes<'node>(
92 return; 123 sema: &Semantics<RootDatabase>,
124 node: &'node SyntaxNode,
125) -> Option<(AttrsOwnerNode, hir::Attrs, Definition)> {
126 match_ast! {
127 match node {
128 ast::SourceFile(it) => sema.to_def(&it).map(|def| (AttrsOwnerNode::new(it), def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Module(def)))),
129 ast::Module(it) => sema.to_def(&it).map(|def| (AttrsOwnerNode::new(it), def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Module(def)))),
130 ast::Fn(it) => sema.to_def(&it).map(|def| (AttrsOwnerNode::new(it), def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Function(def)))),
131 ast::Struct(it) => sema.to_def(&it).map(|def| (AttrsOwnerNode::new(it), def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Struct(def))))),
132 ast::Union(it) => sema.to_def(&it).map(|def| (AttrsOwnerNode::new(it), def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Union(def))))),
133 ast::Enum(it) => sema.to_def(&it).map(|def| (AttrsOwnerNode::new(it), def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Enum(def))))),
134 ast::Variant(it) => sema.to_def(&it).map(|def| (AttrsOwnerNode::new(it), def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Variant(def)))),
135 ast::Trait(it) => sema.to_def(&it).map(|def| (AttrsOwnerNode::new(it), def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Trait(def)))),
136 ast::Static(it) => sema.to_def(&it).map(|def| (AttrsOwnerNode::new(it), def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Static(def)))),
137 ast::Const(it) => sema.to_def(&it).map(|def| (AttrsOwnerNode::new(it), def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Const(def)))),
138 ast::TypeAlias(it) => sema.to_def(&it).map(|def| (AttrsOwnerNode::new(it), def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::TypeAlias(def)))),
139 ast::Impl(it) => sema.to_def(&it).map(|def| (AttrsOwnerNode::new(it), def.attrs(sema.db), Definition::SelfType(def))),
140 ast::RecordField(it) => sema.to_def(&it).map(|def| (AttrsOwnerNode::new(it), def.attrs(sema.db), Definition::Field(def))),
141 ast::TupleField(it) => sema.to_def(&it).map(|def| (AttrsOwnerNode::new(it), def.attrs(sema.db), Definition::Field(def))),
142 ast::MacroRules(it) => sema.to_def(&it).map(|def| (AttrsOwnerNode::new(it), def.attrs(sema.db), Definition::Macro(def))),
143 // ast::MacroDef(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))),
144 // ast::Use(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))),
145 _ => return None
146 }
93 } 147 }
148}
149
150/// Injection of syntax highlighting of doctests.
151pub(super) fn doc_comment(hl: &mut Highlights, sema: &Semantics<RootDatabase>, node: &SyntaxNode) {
152 let (owner, attributes, def) = match doc_attributes(sema, node) {
153 Some(it) => it,
154 None => return,
155 };
94 156
95 let mut inj = Injector::default(); 157 let mut inj = Injector::default();
96 inj.add_unmapped("fn doctest() {\n"); 158 inj.add_unmapped("fn doctest() {\n");
97 159
160 let attrs_source_map = attributes.source_map(&owner);
161
98 let mut is_codeblock = false; 162 let mut is_codeblock = false;
99 let mut is_doctest = false; 163 let mut is_doctest = false;
100 164
101 // Replace the original, line-spanning comment ranges by new, only comment-prefix 165 // Replace the original, line-spanning comment ranges by new, only comment-prefix
102 // spanning comment ranges. 166 // spanning comment ranges.
103 let mut new_comments = Vec::new(); 167 let mut new_comments = Vec::new();
104 for comment in doc_comments { 168 let mut intra_doc_links = Vec::new();
105 match comment.text().find(RUSTDOC_FENCE) { 169 let mut string;
106 Some(idx) => { 170 for attr in attributes.by_key("doc").attrs() {
107 is_codeblock = !is_codeblock; 171 let src = attrs_source_map.source_of(&attr);
108 // Check whether code is rust by inspecting fence guards 172 let (line, range, prefix) = match &src {
109 let guards = &comment.text()[idx + RUSTDOC_FENCE.len()..]; 173 Either::Left(it) => {
110 let is_rust = 174 string = match find_doc_string_in_attr(attr, it) {
111 guards.split(',').all(|sub| RUSTDOC_FENCE_TOKENS.contains(&sub.trim())); 175 Some(it) => it,
112 is_doctest = is_codeblock && is_rust; 176 None => continue,
113 continue; 177 };
178 let text_range = string.syntax().text_range();
179 let text_range = TextRange::new(
180 text_range.start() + TextSize::from(1),
181 text_range.end() - TextSize::from(1),
182 );
183 let text = string.text();
184 (&text[1..text.len() - 1], text_range, "")
114 } 185 }
115 None if !is_doctest => continue, 186 Either::Right(comment) => {
116 None => (), 187 (comment.text(), comment.syntax().text_range(), comment.prefix())
117 } 188 }
189 };
118 190
119 let line: &str = comment.text(); 191 let mut pos = TextSize::from(prefix.len() as u32);
120 let range = comment.syntax().text_range(); 192 let mut range_start = range.start();
193 for line in line.split('\n') {
194 let line_len = TextSize::from(line.len() as u32);
195 let prev_range_start = {
196 let next_range_start = range_start + line_len + TextSize::from(1);
197 mem::replace(&mut range_start, next_range_start)
198 };
199 // only first line has the prefix so take it away for future iterations
200 let mut pos = mem::take(&mut pos);
121 201
122 let mut pos = TextSize::of(comment.prefix()); 202 match line.find(RUSTDOC_FENCE) {
123 // whitespace after comment is ignored 203 Some(idx) => {
124 if let Some(ws) = line[pos.into()..].chars().next().filter(|c| c.is_whitespace()) { 204 is_codeblock = !is_codeblock;
125 pos += TextSize::of(ws); 205 // Check whether code is rust by inspecting fence guards
126 } 206 let guards = &line[idx + RUSTDOC_FENCE.len()..];
127 // lines marked with `#` should be ignored in output, we skip the `#` char 207 let is_rust =
128 if let Some(ws) = line[pos.into()..].chars().next().filter(|&c| c == '#') { 208 guards.split(',').all(|sub| RUSTDOC_FENCE_TOKENS.contains(&sub.trim()));
129 pos += TextSize::of(ws); 209 is_doctest = is_codeblock && is_rust;
210 continue;
211 }
212 None if !is_doctest => {
213 intra_doc_links.extend(
214 extract_definitions_from_markdown(line)
215 .into_iter()
216 .filter(|(link, ns, _)| {
217 validate_intra_doc_link(sema.db, &def, link, *ns)
218 })
219 .map(|(.., Range { start, end })| {
220 TextRange::at(
221 prev_range_start + TextSize::from(start as u32),
222 TextSize::from((end - start) as u32),
223 )
224 }),
225 );
226 continue;
227 }
228 None => (),
229 }
230
231 // whitespace after comment is ignored
232 if let Some(ws) = line[pos.into()..].chars().next().filter(|c| c.is_whitespace()) {
233 pos += TextSize::of(ws);
234 }
235 // lines marked with `#` should be ignored in output, we skip the `#` char
236 if line[pos.into()..].starts_with('#') {
237 pos += TextSize::of('#');
238 }
239
240 new_comments.push(TextRange::at(prev_range_start, pos));
241 inj.add(&line[pos.into()..], TextRange::new(pos, line_len) + prev_range_start);
242 inj.add_unmapped("\n");
130 } 243 }
244 }
131 245
132 new_comments.push(TextRange::at(range.start(), pos)); 246 for range in intra_doc_links {
247 hl.add(HlRange {
248 range,
249 highlight: HlTag::IntraDocLink | HlMod::Documentation,
250 binding_hash: None,
251 });
252 }
133 253
134 inj.add(&line[pos.into()..], TextRange::new(range.start() + pos, range.end())); 254 if new_comments.is_empty() {
135 inj.add_unmapped("\n"); 255 return; // no need to run an analysis on an empty file
136 } 256 }
257
137 inj.add_unmapped("\n}"); 258 inj.add_unmapped("\n}");
138 259
139 let (analysis, tmp_file_id) = Analysis::from_single_file(inj.text().to_string()); 260 let (analysis, tmp_file_id) = Analysis::from_single_file(inj.text().to_string());
140 261
141 for h in analysis.with_db(|db| super::highlight(db, tmp_file_id, None, true)).unwrap() { 262 for HlRange { range, highlight, binding_hash } in
142 for r in inj.map_range_up(h.range) { 263 analysis.with_db(|db| super::highlight(db, tmp_file_id, None, true)).unwrap()
143 hl.add(HlRange { 264 {
144 range: r, 265 for range in inj.map_range_up(range) {
145 highlight: h.highlight | HlMod::Injected, 266 hl.add(HlRange { range, highlight: highlight | HlMod::Injected, binding_hash });
146 binding_hash: h.binding_hash,
147 });
148 } 267 }
149 } 268 }
150 269
@@ -156,3 +275,55 @@ pub(super) fn doc_comment(hl: &mut Highlights, node: &SyntaxNode) {
156 }); 275 });
157 } 276 }
158} 277}
278
279fn find_doc_string_in_attr(attr: &hir::Attr, it: &ast::Attr) -> Option<ast::String> {
280 match it.literal() {
281 // #[doc = lit]
282 Some(lit) => match lit.kind() {
283 ast::LiteralKind::String(it) => Some(it),
284 _ => None,
285 },
286 // #[cfg_attr(..., doc = "", ...)]
287 None => {
288 // We gotta hunt the string token manually here
289 let text = attr.string_value()?;
290 // FIXME: We just pick the first string literal that has the same text as the doc attribute
291 // This means technically we might highlight the wrong one
292 it.syntax()
293 .descendants_with_tokens()
294 .filter_map(NodeOrToken::into_token)
295 .filter_map(ast::String::cast)
296 .find(|string| {
297 string.text().get(1..string.text().len() - 1).map_or(false, |it| it == text)
298 })
299 }
300 }
301}
302
303fn validate_intra_doc_link(
304 db: &RootDatabase,
305 def: &Definition,
306 link: &str,
307 ns: Option<hir::Namespace>,
308) -> bool {
309 match def {
310 Definition::ModuleDef(def) => match def {
311 hir::ModuleDef::Module(it) => it.resolve_doc_path(db, &link, ns),
312 hir::ModuleDef::Function(it) => it.resolve_doc_path(db, &link, ns),
313 hir::ModuleDef::Adt(it) => it.resolve_doc_path(db, &link, ns),
314 hir::ModuleDef::Variant(it) => it.resolve_doc_path(db, &link, ns),
315 hir::ModuleDef::Const(it) => it.resolve_doc_path(db, &link, ns),
316 hir::ModuleDef::Static(it) => it.resolve_doc_path(db, &link, ns),
317 hir::ModuleDef::Trait(it) => it.resolve_doc_path(db, &link, ns),
318 hir::ModuleDef::TypeAlias(it) => it.resolve_doc_path(db, &link, ns),
319 hir::ModuleDef::BuiltinType(_) => None,
320 },
321 Definition::Macro(it) => it.resolve_doc_path(db, &link, ns),
322 Definition::Field(it) => it.resolve_doc_path(db, &link, ns),
323 Definition::SelfType(_)
324 | Definition::Local(_)
325 | Definition::GenericParam(_)
326 | Definition::Label(_) => None,
327 }
328 .is_some()
329}
diff --git a/crates/ide/src/syntax_highlighting/tags.rs b/crates/ide/src/syntax_highlighting/tags.rs
index 3c02fdb11..ce46e5127 100644
--- a/crates/ide/src/syntax_highlighting/tags.rs
+++ b/crates/ide/src/syntax_highlighting/tags.rs
@@ -18,19 +18,20 @@ pub struct HlMods(u32);
18pub enum HlTag { 18pub enum HlTag {
19 Symbol(SymbolKind), 19 Symbol(SymbolKind),
20 20
21 Attribute,
21 BoolLiteral, 22 BoolLiteral,
22 BuiltinType, 23 BuiltinType,
23 ByteLiteral, 24 ByteLiteral,
24 CharLiteral, 25 CharLiteral,
25 NumericLiteral,
26 StringLiteral,
27 Attribute,
28 Comment, 26 Comment,
29 EscapeSequence, 27 EscapeSequence,
30 FormatSpecifier, 28 FormatSpecifier,
29 IntraDocLink,
31 Keyword, 30 Keyword,
32 Punctuation(HlPunct), 31 NumericLiteral,
33 Operator, 32 Operator,
33 Punctuation(HlPunct),
34 StringLiteral,
34 UnresolvedReference, 35 UnresolvedReference,
35 36
36 // For things which don't have a specific highlight. 37 // For things which don't have a specific highlight.
@@ -116,6 +117,7 @@ impl HlTag {
116 HlTag::Comment => "comment", 117 HlTag::Comment => "comment",
117 HlTag::EscapeSequence => "escape_sequence", 118 HlTag::EscapeSequence => "escape_sequence",
118 HlTag::FormatSpecifier => "format_specifier", 119 HlTag::FormatSpecifier => "format_specifier",
120 HlTag::IntraDocLink => "intra_doc_link",
119 HlTag::Keyword => "keyword", 121 HlTag::Keyword => "keyword",
120 HlTag::Punctuation(punct) => match punct { 122 HlTag::Punctuation(punct) => match punct {
121 HlPunct::Bracket => "bracket", 123 HlPunct::Bracket => "bracket",
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html b/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html
index d421a7803..60c7518af 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html
@@ -7,6 +7,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
7.label { color: #DFAF8F; font-style: italic; } 7.label { color: #DFAF8F; font-style: italic; }
8.comment { color: #7F9F7F; } 8.comment { color: #7F9F7F; }
9.documentation { color: #629755; } 9.documentation { color: #629755; }
10.intra_doc_link { color: #A9C577; }
10.injected { opacity: 0.65 ; } 11.injected { opacity: 0.65 ; }
11.struct, .enum { color: #7CB8BB; } 12.struct, .enum { color: #7CB8BB; }
12.enum_variant { color: #BDE0F3; } 13.enum_variant { color: #BDE0F3; }
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
index 5e877df88..5d802a647 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
@@ -7,6 +7,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
7.label { color: #DFAF8F; font-style: italic; } 7.label { color: #DFAF8F; font-style: italic; }
8.comment { color: #7F9F7F; } 8.comment { color: #7F9F7F; }
9.documentation { color: #629755; } 9.documentation { color: #629755; }
10.intra_doc_link { color: #A9C577; }
10.injected { opacity: 0.65 ; } 11.injected { opacity: 0.65 ; }
11.struct, .enum { color: #7CB8BB; } 12.struct, .enum { color: #7CB8BB; }
12.enum_variant { color: #BDE0F3; } 13.enum_variant { color: #BDE0F3; }
@@ -81,7 +82,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
81 <span class="comment documentation">/// </span><span class="comment injected"> comment */</span> 82 <span class="comment documentation">/// </span><span class="comment injected"> comment */</span>
82 <span class="comment documentation">///</span> 83 <span class="comment documentation">///</span>
83 <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="variable declaration injected">multi_line_string</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="string_literal injected">"Foo</span> 84 <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="variable declaration injected">multi_line_string</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="string_literal injected">"Foo</span>
84 <span class="comment documentation">/// </span><span class="string_literal injected"> bar</span> 85 <span class="comment documentation">/// </span><span class="string_literal injected"> bar</span><span class="escape_sequence injected">\n</span>
85 <span class="comment documentation">/// </span><span class="string_literal injected"> "</span><span class="semicolon injected">;</span> 86 <span class="comment documentation">/// </span><span class="string_literal injected"> "</span><span class="semicolon injected">;</span>
86 <span class="comment documentation">///</span> 87 <span class="comment documentation">///</span>
87 <span class="comment documentation">/// ```</span> 88 <span class="comment documentation">/// ```</span>
@@ -98,6 +99,11 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
98 <span class="brace">}</span> 99 <span class="brace">}</span>
99<span class="brace">}</span> 100<span class="brace">}</span>
100 101
102<span class="comment documentation">/// </span><span class="intra_doc_link documentation">[`Foo`](Foo)</span><span class="comment documentation"> is a struct</span>
103<span class="comment documentation">/// </span><span class="intra_doc_link documentation">[`all_the_links`](all_the_links)</span><span class="comment documentation"> is this function</span>
104<span class="comment documentation">/// [`noop`](noop) is a macro below</span>
105<span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration">all_the_links</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
106
101<span class="comment documentation">/// ```</span> 107<span class="comment documentation">/// ```</span>
102<span class="comment documentation">/// </span><span class="macro injected">noop!</span><span class="parenthesis injected">(</span><span class="numeric_literal injected">1</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span> 108<span class="comment documentation">/// </span><span class="macro injected">noop!</span><span class="parenthesis injected">(</span><span class="numeric_literal injected">1</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span>
103<span class="comment documentation">/// ```</span> 109<span class="comment documentation">/// ```</span>
@@ -105,4 +111,36 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
105 <span class="parenthesis">(</span><span class="punctuation">$</span>expr<span class="colon">:</span>expr<span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="brace">{</span> 111 <span class="parenthesis">(</span><span class="punctuation">$</span>expr<span class="colon">:</span>expr<span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="brace">{</span>
106 <span class="punctuation">$</span>expr 112 <span class="punctuation">$</span>expr
107 <span class="brace">}</span> 113 <span class="brace">}</span>
108<span class="brace">}</span></code></pre> \ No newline at end of file 114<span class="brace">}</span>
115
116<span class="comment documentation">/// ```rust</span>
117<span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="punctuation injected">_</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="function injected">example</span><span class="parenthesis injected">(</span><span class="operator injected">&</span><span class="bracket injected">[</span><span class="numeric_literal injected">1</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">2</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">3</span><span class="bracket injected">]</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span>
118<span class="comment documentation">/// ```</span>
119<span class="comment documentation">///</span>
120<span class="comment documentation">/// ```</span>
121<span class="comment documentation">/// </span><span class="keyword control injected">loop</span><span class="none injected"> </span><span class="brace injected">{</span><span class="brace injected">}</span>
122<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">cfg_attr</span><span class="parenthesis attribute">(</span><span class="attribute attribute">not</span><span class="parenthesis attribute">(</span><span class="attribute attribute">feature </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"false"</span><span class="parenthesis attribute">)</span><span class="comma attribute">,</span><span class="attribute attribute"> doc </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"</span><span class="keyword control injected">loop</span><span class="none injected"> </span><span class="brace injected">{</span><span class="brace injected">}</span><span class="string_literal attribute">"</span><span class="parenthesis attribute">)</span><span class="attribute attribute">]</span>
123<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">doc</span><span class="attribute attribute"> </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"</span><span class="keyword control injected">loop</span><span class="none injected"> </span><span class="brace injected">{</span><span class="brace injected">}</span><span class="string_literal attribute">"</span><span class="attribute attribute">]</span>
124<span class="comment documentation">/// ```</span>
125<span class="comment documentation">///</span>
126<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">cfg_attr</span><span class="parenthesis attribute">(</span><span class="attribute attribute">feature </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"alloc"</span><span class="comma attribute">,</span><span class="attribute attribute"> doc </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"```rust"</span><span class="parenthesis attribute">)</span><span class="attribute attribute">]</span>
127<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">cfg_attr</span><span class="parenthesis attribute">(</span><span class="attribute attribute">not</span><span class="parenthesis attribute">(</span><span class="attribute attribute">feature </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"alloc"</span><span class="parenthesis attribute">)</span><span class="comma attribute">,</span><span class="attribute attribute"> doc </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"```ignore"</span><span class="parenthesis attribute">)</span><span class="attribute attribute">]</span>
128<span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="punctuation injected">_</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="function injected">example</span><span class="parenthesis injected">(</span><span class="operator injected">&</span><span class="none injected">alloc::</span><span class="macro injected">vec!</span><span class="bracket injected">[</span><span class="numeric_literal injected">1</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">2</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">3</span><span class="bracket injected">]</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span>
129<span class="comment documentation">/// ```</span>
130<span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration">mix_and_match</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
131
132<span class="comment documentation">/**
133It is beyond me why you'd use these when you got ///
134```rust
135</span><span class="keyword injected">let</span><span class="none injected"> </span><span class="punctuation injected">_</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="function injected">example</span><span class="parenthesis injected">(</span><span class="operator injected">&</span><span class="bracket injected">[</span><span class="numeric_literal injected">1</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">2</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">3</span><span class="bracket injected">]</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span><span class="comment documentation">
136```
137 */</span>
138<span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration">block_comments</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
139
140<span class="comment documentation">/**
141 Really, I don't get it
142 ```rust
143</span><span class="comment documentation"> </span><span class="none injected"> </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="punctuation injected">_</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="function injected">example</span><span class="parenthesis injected">(</span><span class="operator injected">&</span><span class="bracket injected">[</span><span class="numeric_literal injected">1</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">2</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">3</span><span class="bracket injected">]</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span><span class="comment documentation">
144 ```
145*/</span>
146<span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration">block_comments2</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span></code></pre> \ No newline at end of file
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html b/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html
index 6f7a7ffff..4e312765c 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html
@@ -7,6 +7,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
7.label { color: #DFAF8F; font-style: italic; } 7.label { color: #DFAF8F; font-style: italic; }
8.comment { color: #7F9F7F; } 8.comment { color: #7F9F7F; }
9.documentation { color: #629755; } 9.documentation { color: #629755; }
10.intra_doc_link { color: #A9C577; }
10.injected { opacity: 0.65 ; } 11.injected { opacity: 0.65 ; }
11.struct, .enum { color: #7CB8BB; } 12.struct, .enum { color: #7CB8BB; }
12.enum_variant { color: #BDE0F3; } 13.enum_variant { color: #BDE0F3; }
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html b/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
index 753b535b5..57dfe7509 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
@@ -7,6 +7,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
7.label { color: #DFAF8F; font-style: italic; } 7.label { color: #DFAF8F; font-style: italic; }
8.comment { color: #7F9F7F; } 8.comment { color: #7F9F7F; }
9.documentation { color: #629755; } 9.documentation { color: #629755; }
10.intra_doc_link { color: #A9C577; }
10.injected { opacity: 0.65 ; } 11.injected { opacity: 0.65 ; }
11.struct, .enum { color: #7CB8BB; } 12.struct, .enum { color: #7CB8BB; }
12.enum_variant { color: #BDE0F3; } 13.enum_variant { color: #BDE0F3; }
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
index 66d80c4b6..75dbd0f14 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
@@ -7,6 +7,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
7.label { color: #DFAF8F; font-style: italic; } 7.label { color: #DFAF8F; font-style: italic; }
8.comment { color: #7F9F7F; } 8.comment { color: #7F9F7F; }
9.documentation { color: #629755; } 9.documentation { color: #629755; }
10.intra_doc_link { color: #A9C577; }
10.injected { opacity: 0.65 ; } 11.injected { opacity: 0.65 ; }
11.struct, .enum { color: #7CB8BB; } 12.struct, .enum { color: #7CB8BB; }
12.enum_variant { color: #BDE0F3; } 13.enum_variant { color: #BDE0F3; }
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
index 036cb6c11..423256a20 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
@@ -7,6 +7,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
7.label { color: #DFAF8F; font-style: italic; } 7.label { color: #DFAF8F; font-style: italic; }
8.comment { color: #7F9F7F; } 8.comment { color: #7F9F7F; }
9.documentation { color: #629755; } 9.documentation { color: #629755; }
10.intra_doc_link { color: #A9C577; }
10.injected { opacity: 0.65 ; } 11.injected { opacity: 0.65 ; }
11.struct, .enum { color: #7CB8BB; } 12.struct, .enum { color: #7CB8BB; }
12.enum_variant { color: #BDE0F3; } 13.enum_variant { color: #BDE0F3; }
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlighting.html b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
index 2f983c0b8..fffe8c0f5 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlighting.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
@@ -7,6 +7,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
7.label { color: #DFAF8F; font-style: italic; } 7.label { color: #DFAF8F; font-style: italic; }
8.comment { color: #7F9F7F; } 8.comment { color: #7F9F7F; }
9.documentation { color: #629755; } 9.documentation { color: #629755; }
10.intra_doc_link { color: #A9C577; }
10.injected { opacity: 0.65 ; } 11.injected { opacity: 0.65 ; }
11.struct, .enum { color: #7CB8BB; } 12.struct, .enum { color: #7CB8BB; }
12.enum_variant { color: #BDE0F3; } 13.enum_variant { color: #BDE0F3; }
diff --git a/crates/ide/src/syntax_highlighting/test_data/injection.html b/crates/ide/src/syntax_highlighting/test_data/injection.html
index 78dfec951..34d8deb68 100644
--- a/crates/ide/src/syntax_highlighting/test_data/injection.html
+++ b/crates/ide/src/syntax_highlighting/test_data/injection.html
@@ -7,6 +7,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
7.label { color: #DFAF8F; font-style: italic; } 7.label { color: #DFAF8F; font-style: italic; }
8.comment { color: #7F9F7F; } 8.comment { color: #7F9F7F; }
9.documentation { color: #629755; } 9.documentation { color: #629755; }
10.intra_doc_link { color: #A9C577; }
10.injected { opacity: 0.65 ; } 11.injected { opacity: 0.65 ; }
11.struct, .enum { color: #7CB8BB; } 12.struct, .enum { color: #7CB8BB; }
12.enum_variant { color: #BDE0F3; } 13.enum_variant { color: #BDE0F3; }
diff --git a/crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html b/crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html
index e64f2e5e9..d9ca3a4c4 100644
--- a/crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html
+++ b/crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html
@@ -7,6 +7,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
7.label { color: #DFAF8F; font-style: italic; } 7.label { color: #DFAF8F; font-style: italic; }
8.comment { color: #7F9F7F; } 8.comment { color: #7F9F7F; }
9.documentation { color: #629755; } 9.documentation { color: #629755; }
10.intra_doc_link { color: #A9C577; }
10.injected { opacity: 0.65 ; } 11.injected { opacity: 0.65 ; }
11.struct, .enum { color: #7CB8BB; } 12.struct, .enum { color: #7CB8BB; }
12.enum_variant { color: #BDE0F3; } 13.enum_variant { color: #BDE0F3; }
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs
index 9d0cd1af5..7b2922b0d 100644
--- a/crates/ide/src/syntax_highlighting/tests.rs
+++ b/crates/ide/src/syntax_highlighting/tests.rs
@@ -468,7 +468,7 @@ fn main() {
468} 468}
469 469
470#[test] 470#[test]
471fn test_highlight_doctest() { 471fn test_highlight_doc_comment() {
472 check_highlighting( 472 check_highlighting(
473 r#" 473 r#"
474/// ``` 474/// ```
@@ -516,7 +516,7 @@ impl Foo {
516 /// comment */ 516 /// comment */
517 /// 517 ///
518 /// let multi_line_string = "Foo 518 /// let multi_line_string = "Foo
519 /// bar 519 /// bar\n
520 /// "; 520 /// ";
521 /// 521 ///
522 /// ``` 522 /// ```
@@ -533,6 +533,11 @@ impl Foo {
533 } 533 }
534} 534}
535 535
536/// [`Foo`](Foo) is a struct
537/// [`all_the_links`](all_the_links) is this function
538/// [`noop`](noop) is a macro below
539pub fn all_the_links() {}
540
536/// ``` 541/// ```
537/// noop!(1); 542/// noop!(1);
538/// ``` 543/// ```
@@ -541,6 +546,38 @@ macro_rules! noop {
541 $expr 546 $expr
542 } 547 }
543} 548}
549
550/// ```rust
551/// let _ = example(&[1, 2, 3]);
552/// ```
553///
554/// ```
555/// loop {}
556#[cfg_attr(not(feature = "false"), doc = "loop {}")]
557#[doc = "loop {}"]
558/// ```
559///
560#[cfg_attr(feature = "alloc", doc = "```rust")]
561#[cfg_attr(not(feature = "alloc"), doc = "```ignore")]
562/// let _ = example(&alloc::vec![1, 2, 3]);
563/// ```
564pub fn mix_and_match() {}
565
566/**
567It is beyond me why you'd use these when you got ///
568```rust
569let _ = example(&[1, 2, 3]);
570```
571 */
572pub fn block_comments() {}
573
574/**
575 Really, I don't get it
576 ```rust
577 let _ = example(&[1, 2, 3]);
578 ```
579*/
580pub fn block_comments2() {}
544"# 581"#
545 .trim(), 582 .trim(),
546 expect_file!["./test_data/highlight_doctest.html"], 583 expect_file!["./test_data/highlight_doctest.html"],
diff --git a/crates/ide_completion/src/lib.rs b/crates/ide_completion/src/lib.rs
index 5b7ad38d5..d9ea7b7ea 100644
--- a/crates/ide_completion/src/lib.rs
+++ b/crates/ide_completion/src/lib.rs
@@ -255,7 +255,7 @@ fn foo() {
255 bar.fo$0; 255 bar.fo$0;
256} 256}
257"#, 257"#,
258 DetailAndDocumentation { detail: "fn(&self)", documentation: " Do the foo" }, 258 DetailAndDocumentation { detail: "fn(&self)", documentation: "Do the foo" },
259 ); 259 );
260 } 260 }
261 261
diff --git a/crates/ide_db/src/apply_change.rs b/crates/ide_db/src/apply_change.rs
index 104ee113f..9b590d919 100644
--- a/crates/ide_db/src/apply_change.rs
+++ b/crates/ide_db/src/apply_change.rs
@@ -148,7 +148,7 @@ impl RootDatabase {
148 hir::db::HygieneFrameQuery 148 hir::db::HygieneFrameQuery
149 149
150 // DefDatabase 150 // DefDatabase
151 hir::db::ItemTreeQuery 151 hir::db::FileItemTreeQuery
152 hir::db::BlockDefMapQuery 152 hir::db::BlockDefMapQuery
153 hir::db::CrateDefMapQueryQuery 153 hir::db::CrateDefMapQueryQuery
154 hir::db::StructDataQuery 154 hir::db::StructDataQuery
diff --git a/crates/ide_db/src/call_info.rs b/crates/ide_db/src/call_info.rs
index d4016973c..7e26c3ccf 100644
--- a/crates/ide_db/src/call_info.rs
+++ b/crates/ide_db/src/call_info.rs
@@ -53,15 +53,15 @@ pub fn call_info(db: &RootDatabase, position: FilePosition) -> Option<CallInfo>
53 53
54 match callable.kind() { 54 match callable.kind() {
55 hir::CallableKind::Function(func) => { 55 hir::CallableKind::Function(func) => {
56 res.doc = func.docs(db).map(|it| it.as_str().to_string()); 56 res.doc = func.docs(db).map(|it| it.into());
57 format_to!(res.signature, "fn {}", func.name(db)); 57 format_to!(res.signature, "fn {}", func.name(db));
58 } 58 }
59 hir::CallableKind::TupleStruct(strukt) => { 59 hir::CallableKind::TupleStruct(strukt) => {
60 res.doc = strukt.docs(db).map(|it| it.as_str().to_string()); 60 res.doc = strukt.docs(db).map(|it| it.into());
61 format_to!(res.signature, "struct {}", strukt.name(db)); 61 format_to!(res.signature, "struct {}", strukt.name(db));
62 } 62 }
63 hir::CallableKind::TupleEnumVariant(variant) => { 63 hir::CallableKind::TupleEnumVariant(variant) => {
64 res.doc = variant.docs(db).map(|it| it.as_str().to_string()); 64 res.doc = variant.docs(db).map(|it| it.into());
65 format_to!( 65 format_to!(
66 res.signature, 66 res.signature,
67 "enum {}::{}", 67 "enum {}::{}",
diff --git a/crates/ide_db/src/call_info/tests.rs b/crates/ide_db/src/call_info/tests.rs
index 9f84c253c..75ab3eb6e 100644
--- a/crates/ide_db/src/call_info/tests.rs
+++ b/crates/ide_db/src/call_info/tests.rs
@@ -220,11 +220,11 @@ fn bar() {
220} 220}
221"#, 221"#,
222 expect![[r#" 222 expect![[r#"
223 test 223 test
224 ------ 224 ------
225 fn foo(j: u32) -> u32 225 fn foo(j: u32) -> u32
226 (<j: u32>) 226 (<j: u32>)
227 "#]], 227 "#]],
228 ); 228 );
229} 229}
230 230
@@ -249,19 +249,19 @@ pub fn do() {
249 add_one($0 249 add_one($0
250}"#, 250}"#,
251 expect![[r##" 251 expect![[r##"
252 Adds one to the number given. 252 Adds one to the number given.
253 253
254 # Examples 254 # Examples
255 255
256 ``` 256 ```
257 let five = 5; 257 let five = 5;
258 258
259 assert_eq!(6, my_crate::add_one(5)); 259 assert_eq!(6, my_crate::add_one(5));
260 ``` 260 ```
261 ------ 261 ------
262 fn add_one(x: i32) -> i32 262 fn add_one(x: i32) -> i32
263 (<x: i32>) 263 (<x: i32>)
264 "##]], 264 "##]],
265 ); 265 );
266} 266}
267 267
@@ -291,19 +291,19 @@ pub fn do_it() {
291} 291}
292"#, 292"#,
293 expect![[r##" 293 expect![[r##"
294 Adds one to the number given. 294 Adds one to the number given.
295 295
296 # Examples 296 # Examples
297 297
298 ``` 298 ```
299 let five = 5; 299 let five = 5;
300 300
301 assert_eq!(6, my_crate::add_one(5)); 301 assert_eq!(6, my_crate::add_one(5));
302 ``` 302 ```
303 ------ 303 ------
304 fn add_one(x: i32) -> i32 304 fn add_one(x: i32) -> i32
305 (<x: i32>) 305 (<x: i32>)
306 "##]], 306 "##]],
307 ); 307 );
308} 308}
309 309
@@ -335,13 +335,13 @@ pub fn foo(mut r: WriteHandler<()>) {
335} 335}
336"#, 336"#,
337 expect![[r#" 337 expect![[r#"
338 Method is called when writer finishes. 338 Method is called when writer finishes.
339 339
340 By default this method stops actor's `Context`. 340 By default this method stops actor's `Context`.
341 ------ 341 ------
342 fn finished(&mut self, ctx: &mut {unknown}) 342 fn finished(&mut self, ctx: &mut {unknown})
343 (<ctx: &mut {unknown}>) 343 (<ctx: &mut {unknown}>)
344 "#]], 344 "#]],
345 ); 345 );
346} 346}
347 347
@@ -389,11 +389,11 @@ fn main() {
389} 389}
390"#, 390"#,
391 expect![[r#" 391 expect![[r#"
392 A cool tuple struct 392 A cool tuple struct
393 ------ 393 ------
394 struct S(u32, i32) 394 struct S(u32, i32)
395 (u32, <i32>) 395 (u32, <i32>)
396 "#]], 396 "#]],
397 ); 397 );
398} 398}
399 399
@@ -431,11 +431,11 @@ fn main() {
431} 431}
432"#, 432"#,
433 expect![[r#" 433 expect![[r#"
434 A Variant 434 A Variant
435 ------ 435 ------
436 enum E::A(i32) 436 enum E::A(i32)
437 (<i32>) 437 (<i32>)
438 "#]], 438 "#]],
439 ); 439 );
440} 440}
441 441
diff --git a/crates/parser/src/grammar/params.rs b/crates/parser/src/grammar/params.rs
index e313f6fb7..9e2f02d43 100644
--- a/crates/parser/src/grammar/params.rs
+++ b/crates/parser/src/grammar/params.rs
@@ -41,22 +41,32 @@ fn list_(p: &mut Parser, flavor: Flavor) {
41 FnDef | FnTrait | FnPointer => (T!['('], T![')']), 41 FnDef | FnTrait | FnPointer => (T!['('], T![')']),
42 }; 42 };
43 43
44 let m = p.start(); 44 let list_marker = p.start();
45 p.bump(bra); 45 p.bump(bra);
46 46
47 let mut param_marker = None;
47 if let FnDef = flavor { 48 if let FnDef = flavor {
48 // test self_param_outer_attr 49 // test self_param_outer_attr
49 // fn f(#[must_use] self) {} 50 // fn f(#[must_use] self) {}
50 let m = p.start(); 51 let m = p.start();
51 attributes::outer_attrs(p); 52 attributes::outer_attrs(p);
52 opt_self_param(p, m); 53 match opt_self_param(p, m) {
54 Ok(()) => {}
55 Err(m) => param_marker = Some(m),
56 }
53 } 57 }
54 58
55 while !p.at(EOF) && !p.at(ket) { 59 while !p.at(EOF) && !p.at(ket) {
56 // test param_outer_arg 60 // test param_outer_arg
57 // fn f(#[attr1] pat: Type) {} 61 // fn f(#[attr1] pat: Type) {}
58 let m = p.start(); 62 let m = match param_marker.take() {
59 attributes::outer_attrs(p); 63 Some(m) => m,
64 None => {
65 let m = p.start();
66 attributes::outer_attrs(p);
67 m
68 }
69 };
60 70
61 if !p.at_ts(PARAM_FIRST) { 71 if !p.at_ts(PARAM_FIRST) {
62 p.error("expected value parameter"); 72 p.error("expected value parameter");
@@ -72,8 +82,12 @@ fn list_(p: &mut Parser, flavor: Flavor) {
72 } 82 }
73 } 83 }
74 84
85 if let Some(m) = param_marker {
86 m.abandon(p);
87 }
88
75 p.expect(ket); 89 p.expect(ket);
76 m.complete(p, PARAM_LIST); 90 list_marker.complete(p, PARAM_LIST);
77} 91}
78 92
79const PARAM_FIRST: TokenSet = patterns::PATTERN_FIRST.union(types::TYPE_FIRST); 93const PARAM_FIRST: TokenSet = patterns::PATTERN_FIRST.union(types::TYPE_FIRST);
@@ -153,7 +167,7 @@ fn variadic_param(p: &mut Parser) -> bool {
153// fn d(&'a mut self, x: i32) {} 167// fn d(&'a mut self, x: i32) {}
154// fn e(mut self) {} 168// fn e(mut self) {}
155// } 169// }
156fn opt_self_param(p: &mut Parser, m: Marker) { 170fn opt_self_param(p: &mut Parser, m: Marker) -> Result<(), Marker> {
157 if p.at(T![self]) || p.at(T![mut]) && p.nth(1) == T![self] { 171 if p.at(T![self]) || p.at(T![mut]) && p.nth(1) == T![self] {
158 p.eat(T![mut]); 172 p.eat(T![mut]);
159 self_as_name(p); 173 self_as_name(p);
@@ -176,7 +190,7 @@ fn opt_self_param(p: &mut Parser, m: Marker) {
176 | (T![&], LIFETIME_IDENT, T![self], _) 190 | (T![&], LIFETIME_IDENT, T![self], _)
177 | (T![&], LIFETIME_IDENT, T![mut], T![self]) 191 | (T![&], LIFETIME_IDENT, T![mut], T![self])
178 ) { 192 ) {
179 return m.abandon(p); 193 return Err(m);
180 } 194 }
181 p.bump(T![&]); 195 p.bump(T![&]);
182 if p.at(LIFETIME_IDENT) { 196 if p.at(LIFETIME_IDENT) {
@@ -189,6 +203,7 @@ fn opt_self_param(p: &mut Parser, m: Marker) {
189 if !p.at(T![')']) { 203 if !p.at(T![')']) {
190 p.expect(T![,]); 204 p.expect(T![,]);
191 } 205 }
206 Ok(())
192} 207}
193 208
194fn self_as_name(p: &mut Parser) { 209fn self_as_name(p: &mut Parser) {
diff --git a/crates/rust-analyzer/src/semantic_tokens.rs b/crates/rust-analyzer/src/semantic_tokens.rs
index be0bea00b..0cb7d12a7 100644
--- a/crates/rust-analyzer/src/semantic_tokens.rs
+++ b/crates/rust-analyzer/src/semantic_tokens.rs
@@ -45,15 +45,16 @@ define_semantic_token_types![
45 (BRACKET, "bracket"), 45 (BRACKET, "bracket"),
46 (BUILTIN_TYPE, "builtinType"), 46 (BUILTIN_TYPE, "builtinType"),
47 (CHAR_LITERAL, "characterLiteral"), 47 (CHAR_LITERAL, "characterLiteral"),
48 (COMMA, "comma"),
49 (COLON, "colon"), 48 (COLON, "colon"),
49 (COMMA, "comma"),
50 (CONST_PARAMETER, "constParameter"),
50 (DOT, "dot"), 51 (DOT, "dot"),
51 (ESCAPE_SEQUENCE, "escapeSequence"), 52 (ESCAPE_SEQUENCE, "escapeSequence"),
52 (FORMAT_SPECIFIER, "formatSpecifier"), 53 (FORMAT_SPECIFIER, "formatSpecifier"),
53 (GENERIC, "generic"), 54 (GENERIC, "generic"),
54 (CONST_PARAMETER, "constParameter"), 55 (INTRA_DOC_LINK, "intraDocLink"),
55 (LIFETIME, "lifetime"),
56 (LABEL, "label"), 56 (LABEL, "label"),
57 (LIFETIME, "lifetime"),
57 (PARENTHESIS, "parenthesis"), 58 (PARENTHESIS, "parenthesis"),
58 (PUNCTUATION, "punctuation"), 59 (PUNCTUATION, "punctuation"),
59 (SELF_KEYWORD, "selfKeyword"), 60 (SELF_KEYWORD, "selfKeyword"),
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index c63fe2915..70501618e 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -435,19 +435,20 @@ fn semantic_token_type_and_modifiers(
435 SymbolKind::Trait => lsp_types::SemanticTokenType::INTERFACE, 435 SymbolKind::Trait => lsp_types::SemanticTokenType::INTERFACE,
436 SymbolKind::Macro => lsp_types::SemanticTokenType::MACRO, 436 SymbolKind::Macro => lsp_types::SemanticTokenType::MACRO,
437 }, 437 },
438 HlTag::Attribute => semantic_tokens::ATTRIBUTE,
439 HlTag::BoolLiteral => semantic_tokens::BOOLEAN,
438 HlTag::BuiltinType => semantic_tokens::BUILTIN_TYPE, 440 HlTag::BuiltinType => semantic_tokens::BUILTIN_TYPE,
439 HlTag::None => semantic_tokens::GENERIC,
440 HlTag::ByteLiteral | HlTag::NumericLiteral => lsp_types::SemanticTokenType::NUMBER, 441 HlTag::ByteLiteral | HlTag::NumericLiteral => lsp_types::SemanticTokenType::NUMBER,
441 HlTag::BoolLiteral => semantic_tokens::BOOLEAN,
442 HlTag::StringLiteral => lsp_types::SemanticTokenType::STRING,
443 HlTag::CharLiteral => semantic_tokens::CHAR_LITERAL, 442 HlTag::CharLiteral => semantic_tokens::CHAR_LITERAL,
444 HlTag::Comment => lsp_types::SemanticTokenType::COMMENT, 443 HlTag::Comment => lsp_types::SemanticTokenType::COMMENT,
445 HlTag::Attribute => semantic_tokens::ATTRIBUTE, 444 HlTag::EscapeSequence => semantic_tokens::ESCAPE_SEQUENCE,
446 HlTag::Keyword => lsp_types::SemanticTokenType::KEYWORD,
447 HlTag::UnresolvedReference => semantic_tokens::UNRESOLVED_REFERENCE,
448 HlTag::FormatSpecifier => semantic_tokens::FORMAT_SPECIFIER, 445 HlTag::FormatSpecifier => semantic_tokens::FORMAT_SPECIFIER,
446 HlTag::IntraDocLink => semantic_tokens::INTRA_DOC_LINK,
447 HlTag::Keyword => lsp_types::SemanticTokenType::KEYWORD,
448 HlTag::None => semantic_tokens::GENERIC,
449 HlTag::Operator => lsp_types::SemanticTokenType::OPERATOR, 449 HlTag::Operator => lsp_types::SemanticTokenType::OPERATOR,
450 HlTag::EscapeSequence => semantic_tokens::ESCAPE_SEQUENCE, 450 HlTag::StringLiteral => lsp_types::SemanticTokenType::STRING,
451 HlTag::UnresolvedReference => semantic_tokens::UNRESOLVED_REFERENCE,
451 HlTag::Punctuation(punct) => match punct { 452 HlTag::Punctuation(punct) => match punct {
452 HlPunct::Bracket => semantic_tokens::BRACKET, 453 HlPunct::Bracket => semantic_tokens::BRACKET,
453 HlPunct::Brace => semantic_tokens::BRACE, 454 HlPunct::Brace => semantic_tokens::BRACE,
diff --git a/crates/syntax/src/ast.rs b/crates/syntax/src/ast.rs
index 19261686c..38e0b04ef 100644
--- a/crates/syntax/src/ast.rs
+++ b/crates/syntax/src/ast.rs
@@ -118,7 +118,7 @@ fn test_doc_comment_none() {
118 .ok() 118 .ok()
119 .unwrap(); 119 .unwrap();
120 let module = file.syntax().descendants().find_map(Module::cast).unwrap(); 120 let module = file.syntax().descendants().find_map(Module::cast).unwrap();
121 assert!(module.doc_comment_text().is_none()); 121 assert!(module.doc_comments().doc_comment_text().is_none());
122} 122}
123 123
124#[test] 124#[test]
@@ -133,7 +133,7 @@ fn test_outer_doc_comment_of_items() {
133 .ok() 133 .ok()
134 .unwrap(); 134 .unwrap();
135 let module = file.syntax().descendants().find_map(Module::cast).unwrap(); 135 let module = file.syntax().descendants().find_map(Module::cast).unwrap();
136 assert_eq!("doc", module.doc_comment_text().unwrap()); 136 assert_eq!(" doc", module.doc_comments().doc_comment_text().unwrap());
137} 137}
138 138
139#[test] 139#[test]
@@ -148,7 +148,7 @@ fn test_inner_doc_comment_of_items() {
148 .ok() 148 .ok()
149 .unwrap(); 149 .unwrap();
150 let module = file.syntax().descendants().find_map(Module::cast).unwrap(); 150 let module = file.syntax().descendants().find_map(Module::cast).unwrap();
151 assert!(module.doc_comment_text().is_none()); 151 assert!(module.doc_comments().doc_comment_text().is_none());
152} 152}
153 153
154#[test] 154#[test]
@@ -162,7 +162,7 @@ fn test_doc_comment_of_statics() {
162 .ok() 162 .ok()
163 .unwrap(); 163 .unwrap();
164 let st = file.syntax().descendants().find_map(Static::cast).unwrap(); 164 let st = file.syntax().descendants().find_map(Static::cast).unwrap();
165 assert_eq!("Number of levels", st.doc_comment_text().unwrap()); 165 assert_eq!(" Number of levels", st.doc_comments().doc_comment_text().unwrap());
166} 166}
167 167
168#[test] 168#[test]
@@ -181,7 +181,10 @@ fn test_doc_comment_preserves_indents() {
181 .ok() 181 .ok()
182 .unwrap(); 182 .unwrap();
183 let module = file.syntax().descendants().find_map(Module::cast).unwrap(); 183 let module = file.syntax().descendants().find_map(Module::cast).unwrap();
184 assert_eq!("doc1\n```\nfn foo() {\n // ...\n}\n```", module.doc_comment_text().unwrap()); 184 assert_eq!(
185 " doc1\n ```\n fn foo() {\n // ...\n }\n ```",
186 module.doc_comments().doc_comment_text().unwrap()
187 );
185} 188}
186 189
187#[test] 190#[test]
@@ -198,7 +201,7 @@ fn test_doc_comment_preserves_newlines() {
198 .ok() 201 .ok()
199 .unwrap(); 202 .unwrap();
200 let module = file.syntax().descendants().find_map(Module::cast).unwrap(); 203 let module = file.syntax().descendants().find_map(Module::cast).unwrap();
201 assert_eq!("this\nis\nmod\nfoo", module.doc_comment_text().unwrap()); 204 assert_eq!(" this\n is\n mod\n foo", module.doc_comments().doc_comment_text().unwrap());
202} 205}
203 206
204#[test] 207#[test]
@@ -212,7 +215,7 @@ fn test_doc_comment_single_line_block_strips_suffix() {
212 .ok() 215 .ok()
213 .unwrap(); 216 .unwrap();
214 let module = file.syntax().descendants().find_map(Module::cast).unwrap(); 217 let module = file.syntax().descendants().find_map(Module::cast).unwrap();
215 assert_eq!("this is mod foo", module.doc_comment_text().unwrap()); 218 assert_eq!(" this is mod foo", module.doc_comments().doc_comment_text().unwrap());
216} 219}
217 220
218#[test] 221#[test]
@@ -226,7 +229,7 @@ fn test_doc_comment_single_line_block_strips_suffix_whitespace() {
226 .ok() 229 .ok()
227 .unwrap(); 230 .unwrap();
228 let module = file.syntax().descendants().find_map(Module::cast).unwrap(); 231 let module = file.syntax().descendants().find_map(Module::cast).unwrap();
229 assert_eq!("this is mod foo ", module.doc_comment_text().unwrap()); 232 assert_eq!(" this is mod foo ", module.doc_comments().doc_comment_text().unwrap());
230} 233}
231 234
232#[test] 235#[test]
@@ -245,8 +248,8 @@ fn test_doc_comment_multi_line_block_strips_suffix() {
245 .unwrap(); 248 .unwrap();
246 let module = file.syntax().descendants().find_map(Module::cast).unwrap(); 249 let module = file.syntax().descendants().find_map(Module::cast).unwrap();
247 assert_eq!( 250 assert_eq!(
248 " this\n is\n mod foo\n ", 251 "\n this\n is\n mod foo\n ",
249 module.doc_comment_text().unwrap() 252 module.doc_comments().doc_comment_text().unwrap()
250 ); 253 );
251} 254}
252 255
@@ -259,8 +262,8 @@ fn test_comments_preserve_trailing_whitespace() {
259 .unwrap(); 262 .unwrap();
260 let def = file.syntax().descendants().find_map(Struct::cast).unwrap(); 263 let def = file.syntax().descendants().find_map(Struct::cast).unwrap();
261 assert_eq!( 264 assert_eq!(
262 "Representation of a Realm. \nIn the specification these are called Realm Records.", 265 " Representation of a Realm. \n In the specification these are called Realm Records.",
263 def.doc_comment_text().unwrap() 266 def.doc_comments().doc_comment_text().unwrap()
264 ); 267 );
265} 268}
266 269
@@ -276,7 +279,7 @@ fn test_four_slash_line_comment() {
276 .ok() 279 .ok()
277 .unwrap(); 280 .unwrap();
278 let module = file.syntax().descendants().find_map(Module::cast).unwrap(); 281 let module = file.syntax().descendants().find_map(Module::cast).unwrap();
279 assert_eq!("doc comment", module.doc_comment_text().unwrap()); 282 assert_eq!(" doc comment", module.doc_comments().doc_comment_text().unwrap());
280} 283}
281 284
282#[test] 285#[test]
diff --git a/crates/syntax/src/ast/token_ext.rs b/crates/syntax/src/ast/token_ext.rs
index 977eb8181..6c242d126 100644
--- a/crates/syntax/src/ast/token_ext.rs
+++ b/crates/syntax/src/ast/token_ext.rs
@@ -33,23 +33,20 @@ impl ast::Comment {
33 prefix 33 prefix
34 } 34 }
35 35
36 /// Returns the textual content of a doc comment block as a single string. 36 /// Returns the textual content of a doc comment node as a single string with prefix and suffix
37 /// That is, strips leading `///` (+ optional 1 character of whitespace), 37 /// removed.
38 /// trailing `*/`, trailing whitespace and then joins the lines.
39 pub fn doc_comment(&self) -> Option<&str> { 38 pub fn doc_comment(&self) -> Option<&str> {
40 let kind = self.kind(); 39 let kind = self.kind();
41 match kind { 40 match kind {
42 CommentKind { shape, doc: Some(_) } => { 41 CommentKind { shape, doc: Some(_) } => {
43 let prefix = kind.prefix(); 42 let prefix = kind.prefix();
44 let text = &self.text()[prefix.len()..]; 43 let text = &self.text()[prefix.len()..];
45 let ws = text.chars().next().filter(|c| c.is_whitespace()); 44 let text = if shape == CommentShape::Block {
46 let text = ws.map_or(text, |ws| &text[ws.len_utf8()..]); 45 text.strip_suffix("*/").unwrap_or(text)
47 match shape { 46 } else {
48 CommentShape::Block if text.ends_with("*/") => { 47 text
49 Some(&text[..text.len() - "*/".len()]) 48 };
50 } 49 Some(text)
51 _ => Some(text),
52 }
53 } 50 }
54 _ => None, 51 _ => None,
55 } 52 }
diff --git a/crates/syntax/src/ast/traits.rs b/crates/syntax/src/ast/traits.rs
index 13a769d51..ddd213637 100644
--- a/crates/syntax/src/ast/traits.rs
+++ b/crates/syntax/src/ast/traits.rs
@@ -1,8 +1,6 @@
1//! Various traits that are implemented by ast nodes. 1//! Various traits that are implemented by ast nodes.
2//! 2//!
3//! The implementations are usually trivial, and live in generated.rs 3//! The implementations are usually trivial, and live in generated.rs
4use itertools::Itertools;
5
6use crate::{ 4use crate::{
7 ast::{self, support, AstChildren, AstNode, AstToken}, 5 ast::{self, support, AstChildren, AstNode, AstToken},
8 syntax_node::SyntaxElementChildren, 6 syntax_node::SyntaxElementChildren,
@@ -72,14 +70,10 @@ pub trait AttrsOwner: AstNode {
72 } 70 }
73} 71}
74 72
75pub trait DocCommentsOwner: AstNode { 73pub trait DocCommentsOwner: AttrsOwner {
76 fn doc_comments(&self) -> CommentIter { 74 fn doc_comments(&self) -> CommentIter {
77 CommentIter { iter: self.syntax().children_with_tokens() } 75 CommentIter { iter: self.syntax().children_with_tokens() }
78 } 76 }
79
80 fn doc_comment_text(&self) -> Option<String> {
81 self.doc_comments().doc_comment_text()
82 }
83} 77}
84 78
85impl CommentIter { 79impl CommentIter {
@@ -87,12 +81,12 @@ impl CommentIter {
87 CommentIter { iter: syntax_node.children_with_tokens() } 81 CommentIter { iter: syntax_node.children_with_tokens() }
88 } 82 }
89 83
90 /// Returns the textual content of a doc comment block as a single string. 84 #[cfg(test)]
91 /// That is, strips leading `///` (+ optional 1 character of whitespace),
92 /// trailing `*/`, trailing whitespace and then joins the lines.
93 pub fn doc_comment_text(self) -> Option<String> { 85 pub fn doc_comment_text(self) -> Option<String> {
94 let docs = 86 let docs = itertools::Itertools::join(
95 self.filter_map(|comment| comment.doc_comment().map(ToOwned::to_owned)).join("\n"); 87 &mut self.filter_map(|comment| comment.doc_comment().map(ToOwned::to_owned)),
88 "\n",
89 );
96 if docs.is_empty() { 90 if docs.is_empty() {
97 None 91 None
98 } else { 92 } else {
diff --git a/crates/syntax/test_data/parser/inline/ok/0139_param_outer_arg.rast b/crates/syntax/test_data/parser/inline/ok/0139_param_outer_arg.rast
index 495e4c51b..a84088bf3 100644
--- a/crates/syntax/test_data/parser/inline/ok/0139_param_outer_arg.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0139_param_outer_arg.rast
@@ -6,16 +6,16 @@ [email protected]
6 [email protected] "f" 6 [email protected] "f"
7 [email protected] 7 [email protected]
8 [email protected] "(" 8 [email protected] "("
9 ATTR@5..13 9 PARAM@5..23
10 POUND@5..6 "#" 10 ATTR@5..13
11 L_BRACK@6..7 "[" 11 POUND@5..6 "#"
12 PATH@7..12 12 L_BRACK@6..7 "["
13 PATH_SEGMENT@7..12 13 [email protected]
14 NAME_REF@7..12 14 PATH_SEGMENT@7..12
15 IDENT@7..12 "attr1" 15 NAME_REF@7..12
16 R_BRACK@12..13 "]" 16 IDENT@7..12 "attr1"
17 WHITESPACE@13..14 " " 17 R_BRACK@12..13 "]"
18 PARAM@14..23 18 WHITESPACE@13..14 " "
19 [email protected] 19 [email protected]
20 [email protected] 20 [email protected]
21 [email protected] "pat" 21 [email protected] "pat"
diff --git a/crates/syntax/test_data/parser/ok/0051_parameter_attrs.rast b/crates/syntax/test_data/parser/ok/0051_parameter_attrs.rast
index e10521d85..88470c41c 100644
--- a/crates/syntax/test_data/parser/ok/0051_parameter_attrs.rast
+++ b/crates/syntax/test_data/parser/ok/0051_parameter_attrs.rast
@@ -6,25 +6,25 @@ [email protected]
6 [email protected] "g1" 6 [email protected] "g1"
7 [email protected] 7 [email protected]
8 [email protected] "(" 8 [email protected] "("
9 ATTR@6..14 9 PARAM@6..33
10 POUND@6..7 "#" 10 ATTR@6..14
11 L_BRACK@7..8 "[" 11 POUND@6..7 "#"
12 PATH@8..13 12 L_BRACK@7..8 "["
13 PATH_SEGMENT@8..13 13 [email protected]
14 NAME_REF@8..13 14 PATH_SEGMENT@8..13
15 IDENT@8..13 "attr1" 15 NAME_REF@8..13
16 R_BRACK@13..14 "]" 16 IDENT@8..13 "attr1"
17 WHITESPACE@14..15 " " 17 R_BRACK@13..14 "]"
18 ATTR@15..23 18 WHITESPACE@14..15 " "
19 POUND@15..16 "#" 19 ATTR@15..23
20 L_BRACK@16..17 "[" 20 POUND@15..16 "#"
21 PATH@17..22 21 L_BRACK@16..17 "["
22 PATH_SEGMENT@17..22 22 [email protected]
23 NAME_REF@17..22 23 PATH_SEGMENT@17..22
24 IDENT@17..22 "attr2" 24 NAME_REF@17..22
25 R_BRACK@22..23 "]" 25 IDENT@17..22 "attr2"
26 WHITESPACE@23..24 " " 26 R_BRACK@22..23 "]"
27 PARAM@24..33 27 WHITESPACE@23..24 " "
28 [email protected] 28 [email protected]
29 [email protected] 29 [email protected]
30 [email protected] "pat" 30 [email protected] "pat"
@@ -48,16 +48,16 @@ [email protected]
48 [email protected] "g2" 48 [email protected] "g2"
49 [email protected] 49 [email protected]
50 [email protected] "(" 50 [email protected] "("
51 ATT[email protected]2 51 PARAM@44..58
52 POUND@44..45 "#" 52 ATTR@44..52
53 L_BRACK@45..46 "[" 53 POUND@44..45 "#"
54 PATH@46..51 54 L_BRACK@45..46 "["
55 PATH_SEGMENT@46..51 55 [email protected]
56 NAME_REF@46..51 56 PATH_SEGMENT@46..51
57 IDENT@46..51 "attr1" 57 NAME_REF@46..51
58 R_BRACK@51..52 "]" 58 IDENT@46..51 "attr1"
59 WHITESPACE@52..53 " " 59 R_BRACK@51..52 "]"
60 PARAM@53..58 60 WHITESPACE@52..53 " "
61 [email protected] 61 [email protected]
62 [email protected] 62 [email protected]
63 [email protected] "x" 63 [email protected] "x"
@@ -203,16 +203,16 @@ [email protected]
203 [email protected] "bar" 203 [email protected] "bar"
204 [email protected] 204 [email protected]
205 [email protected] "(" 205 [email protected] "("
206 ATT[email protected]04 206 PARAM@197..211
207 POUND@197..198 "#" 207 ATTR@197..204
208 L_BRACK@198..199 "[" 208 POUND@197..198 "#"
209 PATH@199..203 209 L_BRACK@198..199 "["
210 PATH_SEGMENT@199..203 210 [email protected]
211 NAME_REF@199..203 211 PATH_SEGMENT@199..203
212 IDENT@199..203 "attr" 212 NAME_REF@199..203
213 R_BRACK@203..204 "]" 213 IDENT@199..203 "attr"
214 WHITESPACE@204..205 " " 214 R_BRACK@203..204 "]"
215 PARAM@205..211 215 WHITESPACE@204..205 " "
216 [email protected] 216 [email protected]
217 [email protected] "_" 217 [email protected] "_"
218 [email protected] ":" 218 [email protected] ":"