diff options
Diffstat (limited to 'crates/ra_hir_def/src/attr.rs')
-rw-r--r-- | crates/ra_hir_def/src/attr.rs | 91 |
1 files changed, 91 insertions, 0 deletions
diff --git a/crates/ra_hir_def/src/attr.rs b/crates/ra_hir_def/src/attr.rs new file mode 100644 index 000000000..248f03cdf --- /dev/null +++ b/crates/ra_hir_def/src/attr.rs | |||
@@ -0,0 +1,91 @@ | |||
1 | //! A higher level attributes based on TokenTree, with also some shortcuts. | ||
2 | |||
3 | use std::sync::Arc; | ||
4 | |||
5 | use hir_expand::db::AstDatabase; | ||
6 | use mbe::ast_to_token_tree; | ||
7 | use ra_cfg::CfgOptions; | ||
8 | use ra_syntax::{ | ||
9 | ast::{self, AstNode, AttrsOwner}, | ||
10 | SmolStr, | ||
11 | }; | ||
12 | use tt::Subtree; | ||
13 | |||
14 | use crate::{path::Path, HirFileId, Source}; | ||
15 | |||
16 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
17 | pub struct Attr { | ||
18 | pub(crate) path: Path, | ||
19 | pub(crate) input: Option<AttrInput>, | ||
20 | } | ||
21 | |||
22 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
23 | pub enum AttrInput { | ||
24 | Literal(SmolStr), | ||
25 | TokenTree(Subtree), | ||
26 | } | ||
27 | |||
28 | impl Attr { | ||
29 | pub(crate) fn from_src( | ||
30 | Source { file_id, ast }: Source<ast::Attr>, | ||
31 | db: &impl AstDatabase, | ||
32 | ) -> Option<Attr> { | ||
33 | let path = Path::from_src(Source { file_id, ast: ast.path()? }, db)?; | ||
34 | let input = match ast.input() { | ||
35 | None => None, | ||
36 | Some(ast::AttrInput::Literal(lit)) => { | ||
37 | // FIXME: escape? raw string? | ||
38 | let value = lit.syntax().first_token()?.text().trim_matches('"').into(); | ||
39 | Some(AttrInput::Literal(value)) | ||
40 | } | ||
41 | Some(ast::AttrInput::TokenTree(tt)) => { | ||
42 | Some(AttrInput::TokenTree(ast_to_token_tree(&tt)?.0)) | ||
43 | } | ||
44 | }; | ||
45 | |||
46 | Some(Attr { path, input }) | ||
47 | } | ||
48 | |||
49 | pub fn from_attrs_owner( | ||
50 | file_id: HirFileId, | ||
51 | owner: &dyn AttrsOwner, | ||
52 | db: &impl AstDatabase, | ||
53 | ) -> Option<Arc<[Attr]>> { | ||
54 | let mut attrs = owner.attrs().peekable(); | ||
55 | if attrs.peek().is_none() { | ||
56 | // Avoid heap allocation | ||
57 | return None; | ||
58 | } | ||
59 | Some(attrs.flat_map(|ast| Attr::from_src(Source { file_id, ast }, db)).collect()) | ||
60 | } | ||
61 | |||
62 | pub fn is_simple_atom(&self, name: &str) -> bool { | ||
63 | // FIXME: Avoid cloning | ||
64 | self.path.as_ident().map_or(false, |s| s.to_string() == name) | ||
65 | } | ||
66 | |||
67 | // FIXME: handle cfg_attr :-) | ||
68 | pub fn as_cfg(&self) -> Option<&Subtree> { | ||
69 | if !self.is_simple_atom("cfg") { | ||
70 | return None; | ||
71 | } | ||
72 | match &self.input { | ||
73 | Some(AttrInput::TokenTree(subtree)) => Some(subtree), | ||
74 | _ => None, | ||
75 | } | ||
76 | } | ||
77 | |||
78 | pub fn as_path(&self) -> Option<&SmolStr> { | ||
79 | if !self.is_simple_atom("path") { | ||
80 | return None; | ||
81 | } | ||
82 | match &self.input { | ||
83 | Some(AttrInput::Literal(it)) => Some(it), | ||
84 | _ => None, | ||
85 | } | ||
86 | } | ||
87 | |||
88 | pub fn is_cfg_enabled(&self, cfg_options: &CfgOptions) -> Option<bool> { | ||
89 | cfg_options.is_cfg_enabled(self.as_cfg()?) | ||
90 | } | ||
91 | } | ||