aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir_def/src/attr.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir_def/src/attr.rs')
-rw-r--r--crates/ra_hir_def/src/attr.rs91
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
3use std::sync::Arc;
4
5use hir_expand::db::AstDatabase;
6use mbe::ast_to_token_tree;
7use ra_cfg::CfgOptions;
8use ra_syntax::{
9 ast::{self, AstNode, AttrsOwner},
10 SmolStr,
11};
12use tt::Subtree;
13
14use crate::{path::Path, HirFileId, Source};
15
16#[derive(Debug, Clone, PartialEq, Eq)]
17pub struct Attr {
18 pub(crate) path: Path,
19 pub(crate) input: Option<AttrInput>,
20}
21
22#[derive(Debug, Clone, PartialEq, Eq)]
23pub enum AttrInput {
24 Literal(SmolStr),
25 TokenTree(Subtree),
26}
27
28impl 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}