diff options
Diffstat (limited to 'crates/hir_def/src/attr.rs')
-rw-r--r-- | crates/hir_def/src/attr.rs | 68 |
1 files changed, 56 insertions, 12 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)) |