More accurate #[derive]
parsing
This now allows full paths to the derive macro
This commit is contained in:
parent
c7b7c37ea5
commit
ea5cc8d07a
@ -5,7 +5,7 @@
|
||||
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::{
|
||||
@ -19,7 +19,7 @@
|
||||
db::DefDatabase,
|
||||
item_tree::{ItemTreeId, ItemTreeNode},
|
||||
nameres::ModuleSource,
|
||||
path::ModPath,
|
||||
path::{ModPath, PathKind},
|
||||
src::HasChildSource,
|
||||
AdtId, AttrDefId, Lookup,
|
||||
};
|
||||
@ -357,6 +357,46 @@ fn from_src(ast: ast::Attr, hygiene: &Hygiene) -> Option<Attr> {
|
||||
};
|
||||
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)]
|
||||
@ -384,7 +424,7 @@ pub fn exists(self) -> bool {
|
||||
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()
|
||||
|
@ -1289,20 +1289,20 @@ fn push_child_module(
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,11 +9,8 @@
|
||||
|
||||
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},
|
||||
@ -56,11 +53,6 @@ pub fn from_segments(kind: PathKind, segments: impl IntoIterator<Item = Name>) -
|
||||
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>,
|
||||
|
@ -152,6 +152,7 @@ macro_rules! known_names {
|
||||
str,
|
||||
// Special names
|
||||
macro_rules,
|
||||
derive,
|
||||
doc,
|
||||
cfg_attr,
|
||||
// Components of known path (value or mod name)
|
||||
|
Loading…
Reference in New Issue
Block a user