Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir_def/src/attr.rs')
-rw-r--r--crates/hir_def/src/attr.rs130
1 files changed, 67 insertions, 63 deletions
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs
index 94b801736c..d080182554 100644
--- a/crates/hir_def/src/attr.rs
+++ b/crates/hir_def/src/attr.rs
@@ -154,18 +154,21 @@ impl RawAttrs {
return smallvec![attr.clone()];
}
- let subtree = match attr.input.as_deref() {
- Some(AttrInput::TokenTree(it, _)) => it,
+ let subtree = match attr.token_tree_value() {
+ Some(it) => it,
_ => return smallvec![attr.clone()],
};
// Input subtree is: `(cfg, $(attr),+)`
// Split it up into a `cfg` subtree and the `attr` subtrees.
// FIXME: There should be a common API for this.
- let mut parts = subtree.token_trees.split(
- |tt| matches!(tt, tt::TokenTree::Leaf(tt::Leaf::Punct(p)) if p.char == ','),
- );
- let cfg = parts.next().unwrap();
+ let mut parts = subtree.token_trees.split(|tt| {
+ matches!(tt, tt::TokenTree::Leaf(tt::Leaf::Punct(Punct { char: ',', .. })))
+ });
+ let cfg = match parts.next() {
+ Some(it) => it,
+ None => return smallvec![],
+ };
let cfg = Subtree { delimiter: subtree.delimiter, token_trees: cfg.to_vec() };
let cfg = CfgExpr::parse(&cfg);
let index = attr.id;
@@ -259,17 +262,8 @@ impl Attrs {
}
pub fn docs(&self) -> Option<Documentation> {
- let docs = self.by_key("doc").attrs().flat_map(|attr| match attr.input.as_deref()? {
- AttrInput::Literal(s) => Some(s),
- AttrInput::TokenTree(..) => None,
- });
- let indent = docs
- .clone()
- .flat_map(|s| s.lines())
- .filter(|line| !line.chars().all(|c| c.is_whitespace()))
- .map(|line| line.chars().take_while(|c| c.is_whitespace()).count())
- .min()
- .unwrap_or(0);
+ let docs = self.by_key("doc").attrs().filter_map(|attr| attr.string_value());
+ let indent = doc_indent(self);
let mut buf = String::new();
for doc in docs {
// str::lines doesn't yield anything for the empty string
@@ -507,18 +501,9 @@ impl AttrsWithOwner {
&self,
db: &dyn DefDatabase,
) -> Option<(Documentation, DocsRangeMap)> {
- // FIXME: code duplication in `docs` above
- let docs = self.by_key("doc").attrs().flat_map(|attr| match attr.input.as_deref()? {
- AttrInput::Literal(s) => Some((s, attr.id)),
- AttrInput::TokenTree(..) => None,
- });
- let indent = docs
- .clone()
- .flat_map(|(s, _)| s.lines())
- .filter(|line| !line.chars().all(|c| c.is_whitespace()))
- .map(|line| line.chars().take_while(|c| c.is_whitespace()).count())
- .min()
- .unwrap_or(0);
+ let docs =
+ self.by_key("doc").attrs().filter_map(|attr| attr.string_value().map(|s| (s, attr.id)));
+ let indent = doc_indent(self);
let mut buf = String::new();
let mut mapping = Vec::new();
for (doc, idx) in docs {
@@ -557,6 +542,18 @@ impl AttrsWithOwner {
}
}
+fn doc_indent(attrs: &Attrs) -> usize {
+ attrs
+ .by_key("doc")
+ .attrs()
+ .filter_map(|attr| attr.string_value())
+ .flat_map(|s| s.lines())
+ .filter(|line| !line.chars().all(|c| c.is_whitespace()))
+ .map(|line| line.chars().take_while(|c| c.is_whitespace()).count())
+ .min()
+ .unwrap_or(0)
+}
+
fn inner_attributes(
syntax: &SyntaxNode,
) -> Option<impl Iterator<Item = Either<ast::Attr, ast::Comment>>> {
@@ -773,45 +770,58 @@ impl Attr {
Self::from_src(db, ast, hygiene, id)
}
+ pub fn path(&self) -> &ModPath {
+ &self.path
+ }
+}
+
+impl Attr {
+ /// #[path = "string"]
+ pub fn string_value(&self) -> Option<&SmolStr> {
+ match self.input.as_deref()? {
+ AttrInput::Literal(it) => Some(it),
+ _ => None,
+ }
+ }
+
+ /// #[path(ident)]
+ pub fn single_ident_value(&self) -> Option<&tt::Ident> {
+ match self.input.as_deref()? {
+ AttrInput::TokenTree(subtree, _) => match &*subtree.token_trees {
+ [tt::TokenTree::Leaf(tt::Leaf::Ident(ident))] => Some(ident),
+ _ => None,
+ },
+ _ => None,
+ }
+ }
+
+ /// #[path TokenTree]
+ pub fn token_tree_value(&self) -> Option<&Subtree> {
+ match self.input.as_deref()? {
+ AttrInput::TokenTree(subtree, _) => Some(subtree),
+ _ => None,
+ }
+ }
+
/// Parses this attribute as a token tree consisting of comma separated paths.
pub fn parse_path_comma_token_tree(&self) -> Option<impl Iterator<Item = ModPath> + '_> {
- let args = match self.input.as_deref() {
- Some(AttrInput::TokenTree(args, _)) => args,
- _ => return None,
- };
+ let args = self.token_tree_value()?;
if args.delimiter_kind() != Some(DelimiterKind::Parenthesis) {
return None;
}
let paths = args
.token_trees
- .iter()
- .group_by(|tt| {
- matches!(tt, tt::TokenTree::Leaf(tt::Leaf::Punct(Punct { char: ',', .. })))
- })
- .into_iter()
- .filter(|(comma, _)| !*comma)
- .map(|(_, tts)| {
- let segments = tts.filter_map(|tt| match tt {
+ .split(|tt| matches!(tt, tt::TokenTree::Leaf(tt::Leaf::Punct(Punct { char: ',', .. }))))
+ .map(|tts| {
+ let segments = tts.iter().filter_map(|tt| match tt {
tt::TokenTree::Leaf(tt::Leaf::Ident(id)) => Some(id.as_name()),
_ => None,
});
ModPath::from_segments(PathKind::Plain, segments)
- })
- .collect::<Vec<_>>();
-
- Some(paths.into_iter())
- }
-
- pub fn path(&self) -> &ModPath {
- &self.path
- }
+ });
- pub fn string_value(&self) -> Option<&SmolStr> {
- match self.input.as_deref()? {
- AttrInput::Literal(it) => Some(it),
- _ => None,
- }
+ Some(paths)
}
}
@@ -823,17 +833,11 @@ pub struct AttrQuery<'attr> {
impl<'attr> AttrQuery<'attr> {
pub fn tt_values(self) -> impl Iterator<Item = &'attr Subtree> {
- self.attrs().filter_map(|attr| match attr.input.as_deref()? {
- AttrInput::TokenTree(it, _) => Some(it),
- _ => None,
- })
+ self.attrs().filter_map(|attr| attr.token_tree_value())
}
pub fn string_value(self) -> Option<&'attr SmolStr> {
- self.attrs().find_map(|attr| match attr.input.as_deref()? {
- AttrInput::Literal(it) => Some(it),
- _ => None,
- })
+ self.attrs().find_map(|attr| attr.string_value())
}
pub fn exists(self) -> bool {