use base_db::CrateId;
use cfg::{CfgExpr, CfgOptions};
use either::Either;
-use hir_expand::{hygiene::Hygiene, AstId, InFile};
+use hir_expand::{hygiene::Hygiene, name::AsName, AstId, InFile};
use itertools::Itertools;
use mbe::ast_to_token_tree;
use syntax::{
db::DefDatabase,
item_tree::{ItemTreeId, ItemTreeNode},
nameres::ModuleSource,
- path::ModPath,
+ path::{ModPath, PathKind},
src::HasChildSource,
AdtId, AttrDefId, Lookup,
};
};
Some(Attr { path, input })
}
+
+ /// Parses this attribute as a `#[derive]`, returns an iterator that yields all contained paths
+ /// to derive macros.
+ ///
+ /// Returns `None` when the attribute is not a well-formed `#[derive]` attribute.
+ pub(crate) fn parse_derive(&self) -> Option<impl Iterator<Item = ModPath>> {
+ if self.path.as_ident() != Some(&hir_expand::name![derive]) {
+ return None;
+ }
+
+ match &self.input {
+ Some(AttrInput::TokenTree(args)) => {
+ let mut counter = 0;
+ let paths = args
+ .token_trees
+ .iter()
+ .group_by(move |tt| {
+ match tt {
+ tt::TokenTree::Leaf(tt::Leaf::Punct(p)) if p.char == ',' => {
+ counter += 1;
+ }
+ _ => {}
+ }
+ counter
+ })
+ .into_iter()
+ .map(|(_, tts)| {
+ let segments = tts.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())
+ }
+ _ => None,
+ }
+ }
}
#[derive(Debug, Clone, Copy)]
self.attrs().next().is_some()
}
- fn attrs(self) -> impl Iterator<Item = &'a Attr> {
+ pub(crate) fn attrs(self) -> impl Iterator<Item = &'a Attr> {
let key = self.key;
self.attrs
.iter()
}
fn collect_derives(&mut self, attrs: &Attrs, ast_id: FileAstId<ast::Item>) {
- for derive_subtree in attrs.by_key("derive").tt_values() {
- // for #[derive(Copy, Clone)], `derive_subtree` is the `(Copy, Clone)` subtree
- for tt in &derive_subtree.token_trees {
- let ident = match &tt {
- tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => ident,
- tt::TokenTree::Leaf(tt::Leaf::Punct(_)) => continue, // , is ok
- _ => continue, // anything else would be an error (which we currently ignore)
- };
- let path = ModPath::from_tt_ident(ident);
-
- let ast_id = AstIdWithPath::new(self.file_id, ast_id, path);
- self.def_collector
- .unexpanded_attribute_macros
- .push(DeriveDirective { module_id: self.module_id, ast_id });
+ for derive in attrs.by_key("derive").attrs() {
+ match derive.parse_derive() {
+ Some(derive_macros) => {
+ for path in derive_macros {
+ let ast_id = AstIdWithPath::new(self.file_id, ast_id, path);
+ self.def_collector
+ .unexpanded_attribute_macros
+ .push(DeriveDirective { module_id: self.module_id, ast_id });
+ }
+ }
+ None => {
+ // FIXME: diagnose
+ log::debug!("malformed derive: {:?}", derive);
+ }
}
}
}
use crate::{body::LowerCtx, type_ref::LifetimeRef};
use base_db::CrateId;
-use hir_expand::{
- hygiene::Hygiene,
- name::{AsName, Name},
-};
-use syntax::ast::{self};
+use hir_expand::{hygiene::Hygiene, name::Name};
+use syntax::ast;
use crate::{
type_ref::{TypeBound, TypeRef},
ModPath { kind, segments }
}
- /// Converts an `tt::Ident` into a single-identifier `Path`.
- pub(crate) fn from_tt_ident(ident: &tt::Ident) -> ModPath {
- ident.as_name().into()
- }
-
/// Calls `cb` with all paths, represented by this use item.
pub(crate) fn expand_use_item(
item_src: InFile<ast::Use>,
str,
// Special names
macro_rules,
+ derive,
doc,
cfg_attr,
// Components of known path (value or mod name)