diff options
author | Jonas Schievink <[email protected]> | 2020-12-19 00:09:48 +0000 |
---|---|---|
committer | Jonas Schievink <[email protected]> | 2020-12-19 00:10:56 +0000 |
commit | ea5cc8d07ac28a2110b894d154468c3fa46d7040 (patch) | |
tree | d03e228616092a32a75c7c8af624d4e9c7f7fb2f /crates/hir_def/src | |
parent | c7b7c37ea5f25806d8c523e309b7ee9be27f2cde (diff) |
More accurate `#[derive]` parsing
This now allows full paths to the derive macro
Diffstat (limited to 'crates/hir_def/src')
-rw-r--r-- | crates/hir_def/src/attr.rs | 46 | ||||
-rw-r--r-- | crates/hir_def/src/nameres/collector.rs | 28 | ||||
-rw-r--r-- | crates/hir_def/src/path.rs | 12 |
3 files changed, 59 insertions, 27 deletions
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs index 1b9c64ee5..18525406c 100644 --- a/crates/hir_def/src/attr.rs +++ b/crates/hir_def/src/attr.rs | |||
@@ -5,7 +5,7 @@ use std::{ops, sync::Arc}; | |||
5 | use base_db::CrateId; | 5 | use base_db::CrateId; |
6 | use cfg::{CfgExpr, CfgOptions}; | 6 | use cfg::{CfgExpr, CfgOptions}; |
7 | use either::Either; | 7 | use either::Either; |
8 | use hir_expand::{hygiene::Hygiene, AstId, InFile}; | 8 | use hir_expand::{hygiene::Hygiene, name::AsName, AstId, InFile}; |
9 | use itertools::Itertools; | 9 | use itertools::Itertools; |
10 | use mbe::ast_to_token_tree; | 10 | use mbe::ast_to_token_tree; |
11 | use syntax::{ | 11 | use syntax::{ |
@@ -19,7 +19,7 @@ use crate::{ | |||
19 | db::DefDatabase, | 19 | db::DefDatabase, |
20 | item_tree::{ItemTreeId, ItemTreeNode}, | 20 | item_tree::{ItemTreeId, ItemTreeNode}, |
21 | nameres::ModuleSource, | 21 | nameres::ModuleSource, |
22 | path::ModPath, | 22 | path::{ModPath, PathKind}, |
23 | src::HasChildSource, | 23 | src::HasChildSource, |
24 | AdtId, AttrDefId, Lookup, | 24 | AdtId, AttrDefId, Lookup, |
25 | }; | 25 | }; |
@@ -357,6 +357,46 @@ impl Attr { | |||
357 | }; | 357 | }; |
358 | Some(Attr { path, input }) | 358 | Some(Attr { path, input }) |
359 | } | 359 | } |
360 | |||
361 | /// Parses this attribute as a `#[derive]`, returns an iterator that yields all contained paths | ||
362 | /// to derive macros. | ||
363 | /// | ||
364 | /// Returns `None` when the attribute is not a well-formed `#[derive]` attribute. | ||
365 | pub(crate) fn parse_derive(&self) -> Option<impl Iterator<Item = ModPath>> { | ||
366 | if self.path.as_ident() != Some(&hir_expand::name![derive]) { | ||
367 | return None; | ||
368 | } | ||
369 | |||
370 | match &self.input { | ||
371 | Some(AttrInput::TokenTree(args)) => { | ||
372 | let mut counter = 0; | ||
373 | let paths = args | ||
374 | .token_trees | ||
375 | .iter() | ||
376 | .group_by(move |tt| { | ||
377 | match tt { | ||
378 | tt::TokenTree::Leaf(tt::Leaf::Punct(p)) if p.char == ',' => { | ||
379 | counter += 1; | ||
380 | } | ||
381 | _ => {} | ||
382 | } | ||
383 | counter | ||
384 | }) | ||
385 | .into_iter() | ||
386 | .map(|(_, tts)| { | ||
387 | let segments = tts.filter_map(|tt| match tt { | ||
388 | tt::TokenTree::Leaf(tt::Leaf::Ident(id)) => Some(id.as_name()), | ||
389 | _ => None, | ||
390 | }); | ||
391 | ModPath::from_segments(PathKind::Plain, segments) | ||
392 | }) | ||
393 | .collect::<Vec<_>>(); | ||
394 | |||
395 | Some(paths.into_iter()) | ||
396 | } | ||
397 | _ => None, | ||
398 | } | ||
399 | } | ||
360 | } | 400 | } |
361 | 401 | ||
362 | #[derive(Debug, Clone, Copy)] | 402 | #[derive(Debug, Clone, Copy)] |
@@ -384,7 +424,7 @@ impl<'a> AttrQuery<'a> { | |||
384 | self.attrs().next().is_some() | 424 | self.attrs().next().is_some() |
385 | } | 425 | } |
386 | 426 | ||
387 | fn attrs(self) -> impl Iterator<Item = &'a Attr> { | 427 | pub(crate) fn attrs(self) -> impl Iterator<Item = &'a Attr> { |
388 | let key = self.key; | 428 | let key = self.key; |
389 | self.attrs | 429 | self.attrs |
390 | .iter() | 430 | .iter() |
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs index 55228e480..a636ec77d 100644 --- a/crates/hir_def/src/nameres/collector.rs +++ b/crates/hir_def/src/nameres/collector.rs | |||
@@ -1289,20 +1289,20 @@ impl ModCollector<'_, '_> { | |||
1289 | } | 1289 | } |
1290 | 1290 | ||
1291 | fn collect_derives(&mut self, attrs: &Attrs, ast_id: FileAstId<ast::Item>) { | 1291 | fn collect_derives(&mut self, attrs: &Attrs, ast_id: FileAstId<ast::Item>) { |
1292 | for derive_subtree in attrs.by_key("derive").tt_values() { | 1292 | for derive in attrs.by_key("derive").attrs() { |
1293 | // for #[derive(Copy, Clone)], `derive_subtree` is the `(Copy, Clone)` subtree | 1293 | match derive.parse_derive() { |
1294 | for tt in &derive_subtree.token_trees { | 1294 | Some(derive_macros) => { |
1295 | let ident = match &tt { | 1295 | for path in derive_macros { |
1296 | tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => ident, | 1296 | let ast_id = AstIdWithPath::new(self.file_id, ast_id, path); |
1297 | tt::TokenTree::Leaf(tt::Leaf::Punct(_)) => continue, // , is ok | 1297 | self.def_collector |
1298 | _ => continue, // anything else would be an error (which we currently ignore) | 1298 | .unexpanded_attribute_macros |
1299 | }; | 1299 | .push(DeriveDirective { module_id: self.module_id, ast_id }); |
1300 | let path = ModPath::from_tt_ident(ident); | 1300 | } |
1301 | 1301 | } | |
1302 | let ast_id = AstIdWithPath::new(self.file_id, ast_id, path); | 1302 | None => { |
1303 | self.def_collector | 1303 | // FIXME: diagnose |
1304 | .unexpanded_attribute_macros | 1304 | log::debug!("malformed derive: {:?}", derive); |
1305 | .push(DeriveDirective { module_id: self.module_id, ast_id }); | 1305 | } |
1306 | } | 1306 | } |
1307 | } | 1307 | } |
1308 | } | 1308 | } |
diff --git a/crates/hir_def/src/path.rs b/crates/hir_def/src/path.rs index 00a69a8a6..e2bf85bbc 100644 --- a/crates/hir_def/src/path.rs +++ b/crates/hir_def/src/path.rs | |||
@@ -9,11 +9,8 @@ use std::{ | |||
9 | 9 | ||
10 | use crate::{body::LowerCtx, type_ref::LifetimeRef}; | 10 | use crate::{body::LowerCtx, type_ref::LifetimeRef}; |
11 | use base_db::CrateId; | 11 | use base_db::CrateId; |
12 | use hir_expand::{ | 12 | use hir_expand::{hygiene::Hygiene, name::Name}; |
13 | hygiene::Hygiene, | 13 | use syntax::ast; |
14 | name::{AsName, Name}, | ||
15 | }; | ||
16 | use syntax::ast::{self}; | ||
17 | 14 | ||
18 | use crate::{ | 15 | use crate::{ |
19 | type_ref::{TypeBound, TypeRef}, | 16 | type_ref::{TypeBound, TypeRef}, |
@@ -56,11 +53,6 @@ impl ModPath { | |||
56 | ModPath { kind, segments } | 53 | ModPath { kind, segments } |
57 | } | 54 | } |
58 | 55 | ||
59 | /// Converts an `tt::Ident` into a single-identifier `Path`. | ||
60 | pub(crate) fn from_tt_ident(ident: &tt::Ident) -> ModPath { | ||
61 | ident.as_name().into() | ||
62 | } | ||
63 | |||
64 | /// Calls `cb` with all paths, represented by this use item. | 56 | /// Calls `cb` with all paths, represented by this use item. |
65 | pub(crate) fn expand_use_item( | 57 | pub(crate) fn expand_use_item( |
66 | item_src: InFile<ast::Use>, | 58 | item_src: InFile<ast::Use>, |