From 4933beca87a19233cb6de6384da29f081eb05aaf Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sun, 22 Aug 2021 19:12:45 +0200 Subject: [PATCH] Respect attributes in Hygiene token up-mapping --- crates/hir_expand/src/hygiene.rs | 79 ++++++++++++++++++++++---------- crates/hir_expand/src/lib.rs | 10 ++-- 2 files changed, 60 insertions(+), 29 deletions(-) diff --git a/crates/hir_expand/src/hygiene.rs b/crates/hir_expand/src/hygiene.rs index 84852241155..5deb59ae31f 100644 --- a/crates/hir_expand/src/hygiene.rs +++ b/crates/hir_expand/src/hygiene.rs @@ -9,12 +9,15 @@ use db::TokenExpander; use either::Either; use mbe::Origin; use parser::SyntaxKind; -use syntax::{ast, AstNode, SyntaxNode, TextRange, TextSize}; +use syntax::{ + ast::{self, AttrsOwner}, + AstNode, SyntaxNode, TextRange, TextSize, +}; use crate::{ db::{self, AstDatabase}, name::{AsName, Name}, - HirFileId, HirFileIdRepr, InFile, MacroCallLoc, MacroDefKind, MacroFile, + HirFileId, HirFileIdRepr, InFile, MacroCallKind, MacroCallLoc, MacroDefKind, MacroFile, }; #[derive(Clone, Debug)] @@ -121,11 +124,12 @@ impl HygieneFrames { #[derive(Debug, Clone, PartialEq, Eq)] struct HygieneInfo { file: MacroFile, - /// The `macro_rules!` arguments. - def_start: Option>, + /// The start offset of the `macro_rules!` arguments or attribute input. + attr_input_or_mac_def_start: Option>, macro_def: Arc, macro_arg: Arc<(tt::Subtree, mbe::TokenMap)>, + macro_arg_shift: mbe::Shift, exp_map: Arc, } @@ -136,22 +140,34 @@ impl HygieneInfo { token: TextRange, ) -> Option<(InFile, Origin)> { let token_id = self.exp_map.token_by_range(token)?; + let (mut token_id, origin) = self.macro_def.map_id_up(token_id); - let (token_id, origin) = self.macro_def.map_id_up(token_id); - let (token_map, tt) = match origin { - mbe::Origin::Call => { - let call_id = self.file.macro_call_id; - let loc: MacroCallLoc = db.lookup_intern_macro(call_id); - let arg_start = loc.kind.arg(db)?.text_range().start(); - (&self.macro_arg.1, InFile::new(loc.kind.file_id(), arg_start)) - } - mbe::Origin::Def => match (&*self.macro_def, self.def_start) { - ( - TokenExpander::MacroDef { def_site_token_map, .. } - | TokenExpander::MacroRules { def_site_token_map, .. }, - Some(tt), - ) => (def_site_token_map, tt), - _ => panic!("`Origin::Def` used with non-`macro_rules!` macro"), + let loc = db.lookup_intern_macro(self.file.macro_call_id); + + let (token_map, tt) = match &loc.kind { + MacroCallKind::Attr { attr_args, .. } => match self.macro_arg_shift.unshift(token_id) { + Some(unshifted) => { + token_id = unshifted; + (&attr_args.1, self.attr_input_or_mac_def_start?) + } + None => ( + &self.macro_arg.1, + InFile::new(loc.kind.file_id(), loc.kind.arg(db)?.text_range().start()), + ), + }, + _ => match origin { + mbe::Origin::Call => ( + &self.macro_arg.1, + InFile::new(loc.kind.file_id(), loc.kind.arg(db)?.text_range().start()), + ), + mbe::Origin::Def => match (&*self.macro_def, &self.attr_input_or_mac_def_start) { + ( + TokenExpander::MacroDef { def_site_token_map, .. } + | TokenExpander::MacroRules { def_site_token_map, .. }, + Some(tt), + ) => (def_site_token_map, *tt), + _ => panic!("`Origin::Def` used with non-`macro_rules!` macro"), + }, }, }; @@ -165,19 +181,34 @@ fn make_hygiene_info( macro_file: MacroFile, loc: &MacroCallLoc, ) -> Option { - let def_offset = loc.def.ast_id().left().and_then(|id| { + let def = loc.def.ast_id().left().and_then(|id| { let def_tt = match id.to_node(db) { - ast::Macro::MacroRules(mac) => mac.token_tree()?.syntax().text_range().start(), - ast::Macro::MacroDef(mac) => mac.body()?.syntax().text_range().start(), + ast::Macro::MacroRules(mac) => mac.token_tree()?, + ast::Macro::MacroDef(mac) => mac.body()?, }; Some(InFile::new(id.file_id, def_tt)) }); + let attr_input_or_mac_def = def.or_else(|| match loc.kind { + MacroCallKind::Attr { ast_id, invoc_attr_index, .. } => { + let tt = ast_id.to_node(db).attrs().nth(invoc_attr_index as usize)?.token_tree()?; + Some(InFile::new(ast_id.file_id, tt)) + } + _ => None, + }); let macro_def = db.macro_def(loc.def)?; let (_, exp_map) = db.parse_macro_expansion(macro_file).value?; let macro_arg = db.macro_arg(macro_file.macro_call_id)?; - Some(HygieneInfo { file: macro_file, def_start: def_offset, macro_arg, macro_def, exp_map }) + Some(HygieneInfo { + file: macro_file, + attr_input_or_mac_def_start: attr_input_or_mac_def + .map(|it| it.map(|tt| tt.syntax().text_range().start())), + macro_arg_shift: mbe::Shift::new(¯o_arg.0), + macro_arg, + macro_def, + exp_map, + }) } impl HygieneFrame { @@ -214,7 +245,7 @@ impl HygieneFrame { Some(it) => it, }; - let def_site = info.def_start.map(|it| db.hygiene_frame(it.file_id)); + let def_site = info.attr_input_or_mac_def_start.map(|it| db.hygiene_frame(it.file_id)); let call_site = Some(db.hygiene_frame(calling_file)); HygieneFrame { expansion: Some(info), local_inner, krate, call_site, def_site } diff --git a/crates/hir_expand/src/lib.rs b/crates/hir_expand/src/lib.rs index ebf458fe359..40380e1df17 100644 --- a/crates/hir_expand/src/lib.rs +++ b/crates/hir_expand/src/lib.rs @@ -133,13 +133,13 @@ impl HirFileId { }; Some(InFile::new(id.file_id, def_tt)) }); - let def_or_attr_input = def.or_else(|| match loc.kind { + let attr_input_or_mac_def = def.or_else(|| match loc.kind { MacroCallKind::Attr { ast_id, invoc_attr_index, .. } => { let tt = ast_id .to_node(db) .attrs() - .nth(invoc_attr_index as usize) - .and_then(|attr| attr.token_tree())?; + .nth(invoc_attr_index as usize)? + .token_tree()?; Some(InFile::new(ast_id.file_id, tt)) } _ => None, @@ -152,7 +152,7 @@ impl HirFileId { Some(ExpansionInfo { expanded: InFile::new(self, parse.syntax_node()), arg: InFile::new(loc.kind.file_id(), arg_tt), - attr_input_or_mac_def: def_or_attr_input, + attr_input_or_mac_def, macro_arg_shift: mbe::Shift::new(¯o_arg.0), macro_arg, macro_def, @@ -443,7 +443,7 @@ impl ExpansionInfo { }, _ => match origin { mbe::Origin::Call => (&self.macro_arg.1, self.arg.clone()), - mbe::Origin::Def => match (&*self.macro_def, self.attr_input_or_mac_def.as_ref()) { + mbe::Origin::Def => match (&*self.macro_def, &self.attr_input_or_mac_def) { ( TokenExpander::MacroRules { def_site_token_map, .. } | TokenExpander::MacroDef { def_site_token_map, .. },