More accurate #[derive] parsing

This now allows full paths to the derive macro
This commit is contained in:
Jonas Schievink 2020-12-19 01:09:48 +01:00
parent c7b7c37ea5
commit ea5cc8d07a
4 changed files with 60 additions and 27 deletions

View File

@ -5,7 +5,7 @@
use base_db::CrateId; use base_db::CrateId;
use cfg::{CfgExpr, CfgOptions}; use cfg::{CfgExpr, CfgOptions};
use either::Either; use either::Either;
use hir_expand::{hygiene::Hygiene, AstId, InFile}; use hir_expand::{hygiene::Hygiene, name::AsName, AstId, InFile};
use itertools::Itertools; use itertools::Itertools;
use mbe::ast_to_token_tree; use mbe::ast_to_token_tree;
use syntax::{ use syntax::{
@ -19,7 +19,7 @@
db::DefDatabase, db::DefDatabase,
item_tree::{ItemTreeId, ItemTreeNode}, item_tree::{ItemTreeId, ItemTreeNode},
nameres::ModuleSource, nameres::ModuleSource,
path::ModPath, path::{ModPath, PathKind},
src::HasChildSource, src::HasChildSource,
AdtId, AttrDefId, Lookup, AdtId, AttrDefId, Lookup,
}; };
@ -357,6 +357,46 @@ fn from_src(ast: ast::Attr, hygiene: &Hygiene) -> Option<Attr> {
}; };
Some(Attr { path, input }) 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)] #[derive(Debug, Clone, Copy)]
@ -384,7 +424,7 @@ pub fn exists(self) -> bool {
self.attrs().next().is_some() 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; let key = self.key;
self.attrs self.attrs
.iter() .iter()

View File

@ -1289,20 +1289,20 @@ fn push_child_module(
} }
fn collect_derives(&mut self, attrs: &Attrs, ast_id: FileAstId<ast::Item>) { fn collect_derives(&mut self, attrs: &Attrs, ast_id: FileAstId<ast::Item>) {
for derive_subtree in attrs.by_key("derive").tt_values() { for derive in attrs.by_key("derive").attrs() {
// for #[derive(Copy, Clone)], `derive_subtree` is the `(Copy, Clone)` subtree match derive.parse_derive() {
for tt in &derive_subtree.token_trees { Some(derive_macros) => {
let ident = match &tt { for path in derive_macros {
tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => ident, let ast_id = AstIdWithPath::new(self.file_id, ast_id, path);
tt::TokenTree::Leaf(tt::Leaf::Punct(_)) => continue, // , is ok self.def_collector
_ => continue, // anything else would be an error (which we currently ignore) .unexpanded_attribute_macros
}; .push(DeriveDirective { module_id: self.module_id, ast_id });
let path = ModPath::from_tt_ident(ident); }
}
let ast_id = AstIdWithPath::new(self.file_id, ast_id, path); None => {
self.def_collector // FIXME: diagnose
.unexpanded_attribute_macros log::debug!("malformed derive: {:?}", derive);
.push(DeriveDirective { module_id: self.module_id, ast_id }); }
} }
} }
} }

View File

@ -9,11 +9,8 @@
use crate::{body::LowerCtx, type_ref::LifetimeRef}; use crate::{body::LowerCtx, type_ref::LifetimeRef};
use base_db::CrateId; use base_db::CrateId;
use hir_expand::{ use hir_expand::{hygiene::Hygiene, name::Name};
hygiene::Hygiene, use syntax::ast;
name::{AsName, Name},
};
use syntax::ast::{self};
use crate::{ use crate::{
type_ref::{TypeBound, TypeRef}, type_ref::{TypeBound, TypeRef},
@ -56,11 +53,6 @@ pub fn from_segments(kind: PathKind, segments: impl IntoIterator<Item = Name>) -
ModPath { kind, segments } 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. /// Calls `cb` with all paths, represented by this use item.
pub(crate) fn expand_use_item( pub(crate) fn expand_use_item(
item_src: InFile<ast::Use>, item_src: InFile<ast::Use>,

View File

@ -152,6 +152,7 @@ macro_rules! known_names {
str, str,
// Special names // Special names
macro_rules, macro_rules,
derive,
doc, doc,
cfg_attr, cfg_attr,
// Components of known path (value or mod name) // Components of known path (value or mod name)