diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2020-12-08 13:23:12 +0000 |
---|---|---|
committer | GitHub <[email protected]> | 2020-12-08 13:23:12 +0000 |
commit | 2aa7f2ece517a5202421f7a4a7cdd99bd1862ac8 (patch) | |
tree | fcdf682d2175735262a7d733043bb510fd86526e /crates/hir_def/src | |
parent | 4d4f11925f793c45560c45c088d4b3139c2c171c (diff) | |
parent | 3174e941dbb7d91bad011ba51a9b55736996b36c (diff) |
Merge #6750
6750: Remove documentation query, move doc handling to attributes r=matklad a=Veykril
Fixes #3182
Removes the documentation query in favor of `Attrs::docs`. Attrs already handlded doc comments partially but the alloc saving check was wrong so it only worked when other attributes existed as well. Unfortunately the `new` constructor has to do an intermediate allocation now because we need to keep the order of mixed doc attributes and doc comments.
I've also partially adjusted the `hover` module to have its tests check the changes, it still has some `HasSource` trait usage due to the `ShortLabel` trait usage, as that is only implemented on the Ast parts and not the Hir, should this ideally be implemented for the Hir types as well?(would be a follow up PR of course)
Co-authored-by: Lukas Wirth <[email protected]>
Diffstat (limited to 'crates/hir_def/src')
-rw-r--r-- | crates/hir_def/src/attr.rs | 68 | ||||
-rw-r--r-- | crates/hir_def/src/db.rs | 6 | ||||
-rw-r--r-- | crates/hir_def/src/docs.rs | 121 | ||||
-rw-r--r-- | crates/hir_def/src/lib.rs | 1 | ||||
-rw-r--r-- | crates/hir_def/src/nameres/tests/mod_resolution.rs | 2 |
5 files changed, 57 insertions, 141 deletions
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs index b2ce7ca3c..12f4b02e2 100644 --- a/crates/hir_def/src/attr.rs +++ b/crates/hir_def/src/attr.rs | |||
@@ -5,10 +5,11 @@ use std::{ops, sync::Arc}; | |||
5 | use cfg::{CfgExpr, CfgOptions}; | 5 | use cfg::{CfgExpr, CfgOptions}; |
6 | use either::Either; | 6 | use either::Either; |
7 | use hir_expand::{hygiene::Hygiene, AstId, InFile}; | 7 | use hir_expand::{hygiene::Hygiene, AstId, InFile}; |
8 | use itertools::Itertools; | ||
8 | use mbe::ast_to_token_tree; | 9 | use mbe::ast_to_token_tree; |
9 | use syntax::{ | 10 | use syntax::{ |
10 | ast::{self, AstNode, AttrsOwner}, | 11 | ast::{self, AstNode, AttrsOwner}, |
11 | SmolStr, | 12 | AstToken, SmolStr, |
12 | }; | 13 | }; |
13 | use tt::Subtree; | 14 | use tt::Subtree; |
14 | 15 | ||
@@ -21,6 +22,22 @@ use crate::{ | |||
21 | AdtId, AttrDefId, Lookup, | 22 | AdtId, AttrDefId, Lookup, |
22 | }; | 23 | }; |
23 | 24 | ||
25 | /// Holds documentation | ||
26 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
27 | pub struct Documentation(String); | ||
28 | |||
29 | impl Documentation { | ||
30 | pub fn as_str(&self) -> &str { | ||
31 | &self.0 | ||
32 | } | ||
33 | } | ||
34 | |||
35 | impl Into<String> for Documentation { | ||
36 | fn into(self) -> String { | ||
37 | self.0 | ||
38 | } | ||
39 | } | ||
40 | |||
24 | #[derive(Default, Debug, Clone, PartialEq, Eq)] | 41 | #[derive(Default, Debug, Clone, PartialEq, Eq)] |
25 | pub struct Attrs { | 42 | pub struct Attrs { |
26 | entries: Option<Arc<[Attr]>>, | 43 | entries: Option<Arc<[Attr]>>, |
@@ -93,18 +110,25 @@ impl Attrs { | |||
93 | } | 110 | } |
94 | 111 | ||
95 | pub(crate) fn new(owner: &dyn AttrsOwner, hygiene: &Hygiene) -> Attrs { | 112 | pub(crate) fn new(owner: &dyn AttrsOwner, hygiene: &Hygiene) -> Attrs { |
96 | let docs = ast::CommentIter::from_syntax_node(owner.syntax()).doc_comment_text().map( | 113 | let docs = ast::CommentIter::from_syntax_node(owner.syntax()).map(|docs_text| { |
97 | |docs_text| Attr { | 114 | ( |
98 | input: Some(AttrInput::Literal(SmolStr::new(docs_text))), | 115 | docs_text.syntax().text_range().start(), |
99 | path: ModPath::from(hir_expand::name!(doc)), | 116 | docs_text.doc_comment().map(|doc| Attr { |
100 | }, | 117 | input: Some(AttrInput::Literal(SmolStr::new(doc))), |
101 | ); | 118 | path: ModPath::from(hir_expand::name!(doc)), |
102 | let mut attrs = owner.attrs().peekable(); | 119 | }), |
103 | let entries = if attrs.peek().is_none() { | 120 | ) |
121 | }); | ||
122 | let attrs = owner | ||
123 | .attrs() | ||
124 | .map(|attr| (attr.syntax().text_range().start(), Attr::from_src(attr, hygiene))); | ||
125 | // sort here by syntax node offset because the source can have doc attributes and doc strings be interleaved | ||
126 | let attrs: Vec<_> = docs.chain(attrs).sorted_by_key(|&(offset, _)| offset).collect(); | ||
127 | let entries = if attrs.is_empty() { | ||
104 | // Avoid heap allocation | 128 | // Avoid heap allocation |
105 | None | 129 | None |
106 | } else { | 130 | } else { |
107 | Some(attrs.flat_map(|ast| Attr::from_src(ast, hygiene)).chain(docs).collect()) | 131 | Some(attrs.into_iter().flat_map(|(_, attr)| attr).collect()) |
108 | }; | 132 | }; |
109 | Attrs { entries } | 133 | Attrs { entries } |
110 | } | 134 | } |
@@ -140,6 +164,24 @@ impl Attrs { | |||
140 | Some(cfg) => cfg_options.check(&cfg) != Some(false), | 164 | Some(cfg) => cfg_options.check(&cfg) != Some(false), |
141 | } | 165 | } |
142 | } | 166 | } |
167 | |||
168 | pub fn docs(&self) -> Option<Documentation> { | ||
169 | let docs = self | ||
170 | .by_key("doc") | ||
171 | .attrs() | ||
172 | .flat_map(|attr| match attr.input.as_ref()? { | ||
173 | AttrInput::Literal(s) => Some(s), | ||
174 | AttrInput::TokenTree(_) => None, | ||
175 | }) | ||
176 | .intersperse(&SmolStr::new_inline("\n")) | ||
177 | .map(|it| it.as_str()) | ||
178 | .collect::<String>(); | ||
179 | if docs.is_empty() { | ||
180 | None | ||
181 | } else { | ||
182 | Some(Documentation(docs.into())) | ||
183 | } | ||
184 | } | ||
143 | } | 185 | } |
144 | 186 | ||
145 | #[derive(Debug, Clone, PartialEq, Eq)] | 187 | #[derive(Debug, Clone, PartialEq, Eq)] |
@@ -160,8 +202,10 @@ impl Attr { | |||
160 | fn from_src(ast: ast::Attr, hygiene: &Hygiene) -> Option<Attr> { | 202 | fn from_src(ast: ast::Attr, hygiene: &Hygiene) -> Option<Attr> { |
161 | let path = ModPath::from_src(ast.path()?, hygiene)?; | 203 | let path = ModPath::from_src(ast.path()?, hygiene)?; |
162 | let input = if let Some(lit) = ast.literal() { | 204 | let input = if let Some(lit) = ast.literal() { |
163 | // FIXME: escape? raw string? | 205 | let value = match lit.kind() { |
164 | let value = lit.syntax().first_token()?.text().trim_matches('"').into(); | 206 | ast::LiteralKind::String(string) => string.value()?.into(), |
207 | _ => lit.syntax().first_token()?.text().trim_matches('"').into(), | ||
208 | }; | ||
165 | Some(AttrInput::Literal(value)) | 209 | Some(AttrInput::Literal(value)) |
166 | } else if let Some(tt) = ast.token_tree() { | 210 | } else if let Some(tt) = ast.token_tree() { |
167 | Some(AttrInput::TokenTree(ast_to_token_tree(&tt)?.0)) | 211 | Some(AttrInput::TokenTree(ast_to_token_tree(&tt)?.0)) |
diff --git a/crates/hir_def/src/db.rs b/crates/hir_def/src/db.rs index 6d694de11..7f250da33 100644 --- a/crates/hir_def/src/db.rs +++ b/crates/hir_def/src/db.rs | |||
@@ -10,7 +10,6 @@ use crate::{ | |||
10 | attr::Attrs, | 10 | attr::Attrs, |
11 | body::{scope::ExprScopes, Body, BodySourceMap}, | 11 | body::{scope::ExprScopes, Body, BodySourceMap}, |
12 | data::{ConstData, FunctionData, ImplData, StaticData, TraitData, TypeAliasData}, | 12 | data::{ConstData, FunctionData, ImplData, StaticData, TraitData, TypeAliasData}, |
13 | docs::Documentation, | ||
14 | generics::GenericParams, | 13 | generics::GenericParams, |
15 | import_map::ImportMap, | 14 | import_map::ImportMap, |
16 | item_tree::ItemTree, | 15 | item_tree::ItemTree, |
@@ -105,11 +104,6 @@ pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> { | |||
105 | #[salsa::invoke(LangItems::lang_item_query)] | 104 | #[salsa::invoke(LangItems::lang_item_query)] |
106 | fn lang_item(&self, start_crate: CrateId, item: SmolStr) -> Option<LangItemTarget>; | 105 | fn lang_item(&self, start_crate: CrateId, item: SmolStr) -> Option<LangItemTarget>; |
107 | 106 | ||
108 | // FIXME(https://github.com/rust-analyzer/rust-analyzer/issues/2148#issuecomment-550519102) | ||
109 | // Remove this query completely, in favor of `Attrs::docs` method | ||
110 | #[salsa::invoke(Documentation::documentation_query)] | ||
111 | fn documentation(&self, def: AttrDefId) -> Option<Documentation>; | ||
112 | |||
113 | #[salsa::invoke(ImportMap::import_map_query)] | 107 | #[salsa::invoke(ImportMap::import_map_query)] |
114 | fn import_map(&self, krate: CrateId) -> Arc<ImportMap>; | 108 | fn import_map(&self, krate: CrateId) -> Arc<ImportMap>; |
115 | } | 109 | } |
diff --git a/crates/hir_def/src/docs.rs b/crates/hir_def/src/docs.rs deleted file mode 100644 index 3e59a8f47..000000000 --- a/crates/hir_def/src/docs.rs +++ /dev/null | |||
@@ -1,121 +0,0 @@ | |||
1 | //! Defines hir documentation. | ||
2 | //! | ||
3 | //! This really shouldn't exist, instead, we should deshugar doc comments into attributes, see | ||
4 | //! https://github.com/rust-analyzer/rust-analyzer/issues/2148#issuecomment-550519102 | ||
5 | |||
6 | use std::sync::Arc; | ||
7 | |||
8 | use either::Either; | ||
9 | use itertools::Itertools; | ||
10 | use syntax::{ast, SmolStr}; | ||
11 | |||
12 | use crate::{ | ||
13 | db::DefDatabase, | ||
14 | src::{HasChildSource, HasSource}, | ||
15 | AdtId, AttrDefId, Lookup, | ||
16 | }; | ||
17 | |||
18 | /// Holds documentation | ||
19 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
20 | pub struct Documentation(Arc<str>); | ||
21 | |||
22 | impl Into<String> for Documentation { | ||
23 | fn into(self) -> String { | ||
24 | self.as_str().to_owned() | ||
25 | } | ||
26 | } | ||
27 | |||
28 | impl Documentation { | ||
29 | fn new(s: &str) -> Documentation { | ||
30 | Documentation(s.into()) | ||
31 | } | ||
32 | |||
33 | pub fn from_ast<N>(node: &N) -> Option<Documentation> | ||
34 | where | ||
35 | N: ast::DocCommentsOwner + ast::AttrsOwner, | ||
36 | { | ||
37 | docs_from_ast(node) | ||
38 | } | ||
39 | |||
40 | pub fn as_str(&self) -> &str { | ||
41 | &*self.0 | ||
42 | } | ||
43 | |||
44 | pub(crate) fn documentation_query( | ||
45 | db: &dyn DefDatabase, | ||
46 | def: AttrDefId, | ||
47 | ) -> Option<Documentation> { | ||
48 | match def { | ||
49 | AttrDefId::ModuleId(module) => { | ||
50 | let def_map = db.crate_def_map(module.krate); | ||
51 | let src = def_map[module.local_id].declaration_source(db)?; | ||
52 | docs_from_ast(&src.value) | ||
53 | } | ||
54 | AttrDefId::FieldId(it) => { | ||
55 | let src = it.parent.child_source(db); | ||
56 | match &src.value[it.local_id] { | ||
57 | Either::Left(_tuple) => None, | ||
58 | Either::Right(record) => docs_from_ast(record), | ||
59 | } | ||
60 | } | ||
61 | AttrDefId::AdtId(it) => match it { | ||
62 | AdtId::StructId(it) => docs_from_ast(&it.lookup(db).source(db).value), | ||
63 | AdtId::EnumId(it) => docs_from_ast(&it.lookup(db).source(db).value), | ||
64 | AdtId::UnionId(it) => docs_from_ast(&it.lookup(db).source(db).value), | ||
65 | }, | ||
66 | AttrDefId::EnumVariantId(it) => { | ||
67 | let src = it.parent.child_source(db); | ||
68 | docs_from_ast(&src.value[it.local_id]) | ||
69 | } | ||
70 | AttrDefId::TraitId(it) => docs_from_ast(&it.lookup(db).source(db).value), | ||
71 | AttrDefId::MacroDefId(it) => docs_from_ast(&it.ast_id?.to_node(db.upcast())), | ||
72 | AttrDefId::ConstId(it) => docs_from_ast(&it.lookup(db).source(db).value), | ||
73 | AttrDefId::StaticId(it) => docs_from_ast(&it.lookup(db).source(db).value), | ||
74 | AttrDefId::FunctionId(it) => docs_from_ast(&it.lookup(db).source(db).value), | ||
75 | AttrDefId::TypeAliasId(it) => docs_from_ast(&it.lookup(db).source(db).value), | ||
76 | AttrDefId::ImplId(_) => None, | ||
77 | } | ||
78 | } | ||
79 | } | ||
80 | |||
81 | pub(crate) fn docs_from_ast<N>(node: &N) -> Option<Documentation> | ||
82 | where | ||
83 | N: ast::DocCommentsOwner + ast::AttrsOwner, | ||
84 | { | ||
85 | let doc_comment_text = node.doc_comment_text(); | ||
86 | let doc_attr_text = expand_doc_attrs(node); | ||
87 | let docs = merge_doc_comments_and_attrs(doc_comment_text, doc_attr_text); | ||
88 | docs.map(|it| Documentation::new(&it)) | ||
89 | } | ||
90 | |||
91 | fn merge_doc_comments_and_attrs( | ||
92 | doc_comment_text: Option<String>, | ||
93 | doc_attr_text: Option<String>, | ||
94 | ) -> Option<String> { | ||
95 | match (doc_comment_text, doc_attr_text) { | ||
96 | (Some(mut comment_text), Some(attr_text)) => { | ||
97 | comment_text.push_str("\n"); | ||
98 | comment_text.push_str(&attr_text); | ||
99 | Some(comment_text) | ||
100 | } | ||
101 | (Some(comment_text), None) => Some(comment_text), | ||
102 | (None, Some(attr_text)) => Some(attr_text), | ||
103 | (None, None) => None, | ||
104 | } | ||
105 | } | ||
106 | |||
107 | fn expand_doc_attrs(owner: &dyn ast::AttrsOwner) -> Option<String> { | ||
108 | let mut docs = String::new(); | ||
109 | owner | ||
110 | .attrs() | ||
111 | .filter_map(|attr| attr.as_simple_key_value().filter(|(key, _)| key == "doc")) | ||
112 | .map(|(_, value)| value) | ||
113 | .intersperse(SmolStr::new_inline("\n")) | ||
114 | // No FromIterator<SmolStr> for String | ||
115 | .for_each(|s| docs.push_str(s.as_str())); | ||
116 | if docs.is_empty() { | ||
117 | None | ||
118 | } else { | ||
119 | Some(docs) | ||
120 | } | ||
121 | } | ||
diff --git a/crates/hir_def/src/lib.rs b/crates/hir_def/src/lib.rs index b41c5acb2..02ed30e4d 100644 --- a/crates/hir_def/src/lib.rs +++ b/crates/hir_def/src/lib.rs | |||
@@ -31,7 +31,6 @@ pub mod adt; | |||
31 | pub mod data; | 31 | pub mod data; |
32 | pub mod generics; | 32 | pub mod generics; |
33 | pub mod lang_item; | 33 | pub mod lang_item; |
34 | pub mod docs; | ||
35 | 34 | ||
36 | pub mod expr; | 35 | pub mod expr; |
37 | pub mod body; | 36 | pub mod body; |
diff --git a/crates/hir_def/src/nameres/tests/mod_resolution.rs b/crates/hir_def/src/nameres/tests/mod_resolution.rs index ef6f85e15..e80b593aa 100644 --- a/crates/hir_def/src/nameres/tests/mod_resolution.rs +++ b/crates/hir_def/src/nameres/tests/mod_resolution.rs | |||
@@ -372,7 +372,7 @@ fn module_resolution_explicit_path_mod_rs_with_win_separator() { | |||
372 | check( | 372 | check( |
373 | r#" | 373 | r#" |
374 | //- /main.rs | 374 | //- /main.rs |
375 | #[path = "module\bar\mod.rs"] | 375 | #[path = r"module\bar\mod.rs"] |
376 | mod foo; | 376 | mod foo; |
377 | 377 | ||
378 | //- /module/bar/mod.rs | 378 | //- /module/bar/mod.rs |