]> git.lizzy.rs Git - rust.git/commitdiff
More accurate `#[derive]` parsing
authorJonas Schievink <jonasschievink@gmail.com>
Sat, 19 Dec 2020 00:09:48 +0000 (01:09 +0100)
committerJonas Schievink <jonasschievink@gmail.com>
Sat, 19 Dec 2020 00:10:56 +0000 (01:10 +0100)
This now allows full paths to the derive macro

crates/hir_def/src/attr.rs
crates/hir_def/src/nameres/collector.rs
crates/hir_def/src/path.rs
crates/hir_expand/src/name.rs

index 1b9c64ee55ad381c7fe8b4e42fbbee50b23ce784..18525406c92a810e0a196b460af20ecff303e7ad 100644 (file)
@@ -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()
index 55228e480a5f21e5ce795ebb4ceea28f1697294a..a636ec77d71b9dac4f7c4a5e3cf84f5d24e5a291 100644 (file)
@@ -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);
+                }
             }
         }
     }
index 00a69a8a63d23b4ebea0e1c185e1318d5276736b..e2bf85bbc3a145080d5e15c02959275bb6a66e38 100644 (file)
@@ -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>,
index 77eeee3fe9fff44bdc2349d044bca0b36074def1..2f44876a8a3c382320363705d67dda4d821c0458 100644 (file)
@@ -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)