2020-08-13 14:26:29 +00:00
|
|
|
//! `hir_expand` deals with macro expansion.
|
2019-10-29 08:15:51 +00:00
|
|
|
//!
|
2019-10-29 13:01:14 +00:00
|
|
|
//! Specifically, it implements a concept of `MacroFile` -- a file whose syntax
|
|
|
|
//! tree originates not from the text of some `FileId`, but from some macro
|
|
|
|
//! expansion.
|
2019-10-29 08:15:51 +00:00
|
|
|
|
2022-07-20 12:59:42 +00:00
|
|
|
#![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)]
|
|
|
|
|
2019-10-29 11:55:39 +00:00
|
|
|
pub mod db;
|
2019-10-29 08:15:51 +00:00
|
|
|
pub mod ast_id_map;
|
2019-10-30 15:56:20 +00:00
|
|
|
pub mod name;
|
2019-10-30 16:10:53 +00:00
|
|
|
pub mod hygiene;
|
2021-10-10 12:44:03 +00:00
|
|
|
pub mod builtin_attr_macro;
|
|
|
|
pub mod builtin_derive_macro;
|
|
|
|
pub mod builtin_fn_macro;
|
2020-03-18 09:47:59 +00:00
|
|
|
pub mod proc_macro;
|
2019-11-11 06:14:39 +00:00
|
|
|
pub mod quote;
|
2020-03-02 06:05:15 +00:00
|
|
|
pub mod eager;
|
2022-01-26 17:31:07 +00:00
|
|
|
pub mod mod_path;
|
2022-02-07 17:08:31 +00:00
|
|
|
mod fixup;
|
2019-10-29 11:55:39 +00:00
|
|
|
|
2022-02-21 18:14:06 +00:00
|
|
|
pub use mbe::{Origin, ValueResult};
|
2020-11-26 15:48:17 +00:00
|
|
|
|
2022-02-21 18:14:06 +00:00
|
|
|
use std::{fmt, hash::Hash, iter, sync::Arc};
|
2019-10-29 12:11:42 +00:00
|
|
|
|
2022-02-04 01:50:33 +00:00
|
|
|
use base_db::{impl_intern_key, salsa, CrateId, FileId, FileRange, ProcMacroKind};
|
|
|
|
use either::Either;
|
2020-08-12 16:26:51 +00:00
|
|
|
use syntax::{
|
2021-11-03 20:12:36 +00:00
|
|
|
algo::{self, skip_trivia_token},
|
2022-01-07 17:51:10 +00:00
|
|
|
ast::{self, AstNode, HasDocComments},
|
2021-11-03 20:12:36 +00:00
|
|
|
Direction, SyntaxNode, SyntaxToken,
|
2019-11-02 20:11:05 +00:00
|
|
|
};
|
2019-10-29 12:11:42 +00:00
|
|
|
|
2021-08-19 20:53:00 +00:00
|
|
|
use crate::{
|
|
|
|
ast_id_map::FileAstId,
|
2021-10-10 12:44:03 +00:00
|
|
|
builtin_attr_macro::BuiltinAttrExpander,
|
|
|
|
builtin_derive_macro::BuiltinDeriveExpander,
|
|
|
|
builtin_fn_macro::{BuiltinFnLikeExpander, EagerExpander},
|
2021-08-21 16:06:03 +00:00
|
|
|
db::TokenExpander,
|
2022-02-04 01:50:33 +00:00
|
|
|
mod_path::ModPath,
|
2021-08-19 20:53:00 +00:00
|
|
|
proc_macro::ProcMacroExpander,
|
|
|
|
};
|
2019-10-29 12:11:42 +00:00
|
|
|
|
2022-02-21 18:14:06 +00:00
|
|
|
pub type ExpandResult<T> = ValueResult<T, ExpandError>;
|
|
|
|
|
|
|
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
|
|
|
pub enum ExpandError {
|
2022-06-28 08:41:10 +00:00
|
|
|
UnresolvedProcMacro(CrateId),
|
2022-02-21 18:14:06 +00:00
|
|
|
Mbe(mbe::ExpandError),
|
|
|
|
Other(Box<str>),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<mbe::ExpandError> for ExpandError {
|
|
|
|
fn from(mbe: mbe::ExpandError) -> Self {
|
|
|
|
Self::Mbe(mbe)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl fmt::Display for ExpandError {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
|
match self {
|
2022-06-28 08:41:10 +00:00
|
|
|
ExpandError::UnresolvedProcMacro(_) => f.write_str("unresolved proc-macro"),
|
2022-02-21 18:14:06 +00:00
|
|
|
ExpandError::Mbe(it) => it.fmt(f),
|
|
|
|
ExpandError::Other(it) => f.write_str(it),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-29 12:11:42 +00:00
|
|
|
/// Input to the analyzer is a set of files, where each file is identified by
|
|
|
|
/// `FileId` and contains source code. However, another source of source code in
|
|
|
|
/// Rust are macros: each macro can be thought of as producing a "temporary
|
|
|
|
/// file". To assign an id to such a file, we use the id of the macro call that
|
|
|
|
/// produced the file. So, a `HirFileId` is either a `FileId` (source code
|
|
|
|
/// written by user), or a `MacroCallId` (source code produced by macro).
|
|
|
|
///
|
|
|
|
/// What is a `MacroCallId`? Simplifying, it's a `HirFileId` of a file
|
|
|
|
/// containing the call plus the offset of the macro call in the file. Note that
|
|
|
|
/// this is a recursive definition! However, the size_of of `HirFileId` is
|
|
|
|
/// finite (because everything bottoms out at the real `FileId`) and small
|
2020-08-11 09:19:02 +00:00
|
|
|
/// (`MacroCallId` uses the location interning. You can check details here:
|
2021-06-14 04:57:10 +00:00
|
|
|
/// <https://en.wikipedia.org/wiki/String_interning>).
|
2019-10-29 12:11:42 +00:00
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
|
|
pub struct HirFileId(HirFileIdRepr);
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
|
|
enum HirFileIdRepr {
|
|
|
|
FileId(FileId),
|
|
|
|
MacroFile(MacroFile),
|
|
|
|
}
|
2022-02-04 01:50:33 +00:00
|
|
|
|
2019-10-29 12:11:42 +00:00
|
|
|
impl From<FileId> for HirFileId {
|
|
|
|
fn from(id: FileId) -> Self {
|
|
|
|
HirFileId(HirFileIdRepr::FileId(id))
|
|
|
|
}
|
|
|
|
}
|
2022-02-04 01:50:33 +00:00
|
|
|
|
2019-10-29 12:11:42 +00:00
|
|
|
impl From<MacroFile> for HirFileId {
|
|
|
|
fn from(id: MacroFile) -> Self {
|
|
|
|
HirFileId(HirFileIdRepr::MacroFile(id))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-10 16:13:05 +00:00
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
|
|
pub struct MacroFile {
|
|
|
|
pub macro_call_id: MacroCallId,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// `MacroCallId` identifies a particular macro invocation, like
|
|
|
|
/// `println!("Hello, {}", world)`.
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
|
|
pub struct MacroCallId(salsa::InternId);
|
|
|
|
impl_intern_key!(MacroCallId);
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
|
|
pub struct MacroCallLoc {
|
|
|
|
pub def: MacroDefId,
|
|
|
|
pub(crate) krate: CrateId,
|
|
|
|
eager: Option<EagerCallInfo>,
|
|
|
|
pub kind: MacroCallKind,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
|
|
pub struct MacroDefId {
|
|
|
|
pub krate: CrateId,
|
|
|
|
pub kind: MacroDefKind,
|
|
|
|
pub local_inner: bool,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
|
|
pub enum MacroDefKind {
|
|
|
|
Declarative(AstId<ast::Macro>),
|
|
|
|
BuiltIn(BuiltinFnLikeExpander, AstId<ast::Macro>),
|
|
|
|
BuiltInAttr(BuiltinAttrExpander, AstId<ast::Macro>),
|
|
|
|
BuiltInDerive(BuiltinDeriveExpander, AstId<ast::Macro>),
|
|
|
|
BuiltInEager(EagerExpander, AstId<ast::Macro>),
|
|
|
|
ProcMacro(ProcMacroExpander, ProcMacroKind, AstId<ast::Fn>),
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
|
|
struct EagerCallInfo {
|
|
|
|
/// NOTE: This can be *either* the expansion result, *or* the argument to the eager macro!
|
|
|
|
arg_or_expansion: Arc<tt::Subtree>,
|
|
|
|
included_file: Option<FileId>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
|
|
pub enum MacroCallKind {
|
|
|
|
FnLike {
|
|
|
|
ast_id: AstId<ast::MacroCall>,
|
|
|
|
expand_to: ExpandTo,
|
|
|
|
},
|
|
|
|
Derive {
|
2022-01-07 13:19:11 +00:00
|
|
|
ast_id: AstId<ast::Adt>,
|
2021-10-10 16:13:05 +00:00
|
|
|
/// Syntactical index of the invoking `#[derive]` attribute.
|
|
|
|
///
|
|
|
|
/// Outer attributes are counted first, then inner attributes. This does not support
|
|
|
|
/// out-of-line modules, which may have attributes spread across 2 files!
|
|
|
|
derive_attr_index: u32,
|
2022-02-20 23:02:10 +00:00
|
|
|
/// Index of the derive macro in the derive attribute
|
|
|
|
derive_index: u32,
|
2021-10-10 16:13:05 +00:00
|
|
|
},
|
|
|
|
Attr {
|
|
|
|
ast_id: AstId<ast::Item>,
|
2022-02-20 21:53:04 +00:00
|
|
|
attr_args: Arc<(tt::Subtree, mbe::TokenMap)>,
|
2021-10-10 16:13:05 +00:00
|
|
|
/// Syntactical index of the invoking `#[attribute]`.
|
|
|
|
///
|
|
|
|
/// Outer attributes are counted first, then inner attributes. This does not support
|
|
|
|
/// out-of-line modules, which may have attributes spread across 2 files!
|
|
|
|
invoc_attr_index: u32,
|
2022-02-22 09:45:29 +00:00
|
|
|
/// Whether this attribute is the `#[derive]` attribute.
|
2022-02-21 01:42:58 +00:00
|
|
|
is_derive: bool,
|
2021-10-10 16:13:05 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2019-10-29 12:11:42 +00:00
|
|
|
impl HirFileId {
|
|
|
|
/// For macro-expansion files, returns the file original source file the
|
|
|
|
/// expansion originated from.
|
2019-10-30 13:12:55 +00:00
|
|
|
pub fn original_file(self, db: &dyn db::AstDatabase) -> FileId {
|
2022-04-06 11:58:40 +00:00
|
|
|
let mut file_id = self;
|
|
|
|
loop {
|
|
|
|
match file_id.0 {
|
|
|
|
HirFileIdRepr::FileId(id) => break id,
|
|
|
|
HirFileIdRepr::MacroFile(MacroFile { macro_call_id }) => {
|
|
|
|
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_call_id);
|
|
|
|
file_id = match loc.eager {
|
|
|
|
Some(EagerCallInfo { included_file: Some(file), .. }) => file.into(),
|
|
|
|
_ => loc.kind.file_id(),
|
|
|
|
};
|
|
|
|
}
|
2019-10-29 12:11:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-11-03 17:44:23 +00:00
|
|
|
|
2020-07-15 15:18:19 +00:00
|
|
|
pub fn expansion_level(self, db: &dyn db::AstDatabase) -> u32 {
|
|
|
|
let mut level = 0;
|
|
|
|
let mut curr = self;
|
|
|
|
while let HirFileIdRepr::MacroFile(macro_file) = curr.0 {
|
2021-11-14 15:25:40 +00:00
|
|
|
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
|
2021-05-19 18:19:08 +00:00
|
|
|
|
2020-07-15 15:18:19 +00:00
|
|
|
level += 1;
|
2021-05-19 18:19:08 +00:00
|
|
|
curr = loc.kind.file_id();
|
2020-07-15 15:18:19 +00:00
|
|
|
}
|
|
|
|
level
|
|
|
|
}
|
|
|
|
|
2019-12-06 18:30:15 +00:00
|
|
|
/// If this is a macro call, returns the syntax node of the call.
|
|
|
|
pub fn call_node(self, db: &dyn db::AstDatabase) -> Option<InFile<SyntaxNode>> {
|
|
|
|
match self.0 {
|
|
|
|
HirFileIdRepr::FileId(_) => None,
|
2021-05-19 18:19:08 +00:00
|
|
|
HirFileIdRepr::MacroFile(macro_file) => {
|
2021-11-14 15:25:40 +00:00
|
|
|
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
|
internal: move diagnostics to hir
The idea here is to eventually get rid of `dyn Diagnostic` and
`DiagnosticSink` infrastructure altogether, and just have a `enum
hir::Diagnostic` instead.
The problem with `dyn Diagnostic` is that it is defined in the lowest
level of the stack (hir_expand), but is used by the highest level (ide).
As a first step, we free hir_expand and hir_def from `dyn Diagnostic`
and kick the can up to `hir_ty`, as an intermediate state. The plan is
then to move DiagnosticSink similarly to the hir crate, and, as final
third step, remove its usage from the ide.
One currently unsolved problem is testing. You can notice that the test
which checks precise diagnostic ranges, unresolved_import_in_use_tree,
was moved to the ide layer. Logically, only IDE should have the infra to
render a specific range.
At the same time, the range is determined with the data produced in
hir_def and hir crates, so this layering is rather unfortunate. Working
on hir_def shouldn't require compiling `ide` for testing.
2021-05-23 20:31:59 +00:00
|
|
|
Some(loc.kind.to_node(db))
|
2021-05-19 18:19:08 +00:00
|
|
|
}
|
2019-12-06 18:30:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-06 11:58:40 +00:00
|
|
|
/// If this is a macro call, returns the syntax node of the very first macro call this file resides in.
|
|
|
|
pub fn original_call_node(self, db: &dyn db::AstDatabase) -> Option<(FileId, SyntaxNode)> {
|
|
|
|
let mut call = match self.0 {
|
|
|
|
HirFileIdRepr::FileId(_) => return None,
|
|
|
|
HirFileIdRepr::MacroFile(MacroFile { macro_call_id }) => {
|
|
|
|
db.lookup_intern_macro_call(macro_call_id).kind.to_node(db)
|
|
|
|
}
|
|
|
|
};
|
|
|
|
loop {
|
|
|
|
match call.file_id.0 {
|
|
|
|
HirFileIdRepr::FileId(file_id) => break Some((file_id, call.value)),
|
|
|
|
HirFileIdRepr::MacroFile(MacroFile { macro_call_id }) => {
|
|
|
|
call = db.lookup_intern_macro_call(macro_call_id).kind.to_node(db);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-03 17:44:23 +00:00
|
|
|
/// Return expansion information if it is a macro-expansion file
|
2019-11-09 04:00:46 +00:00
|
|
|
pub fn expansion_info(self, db: &dyn db::AstDatabase) -> Option<ExpansionInfo> {
|
2019-11-03 17:44:23 +00:00
|
|
|
match self.0 {
|
|
|
|
HirFileIdRepr::FileId(_) => None,
|
|
|
|
HirFileIdRepr::MacroFile(macro_file) => {
|
2021-11-14 15:25:40 +00:00
|
|
|
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
|
2019-11-03 17:44:23 +00:00
|
|
|
|
2019-12-05 14:10:33 +00:00
|
|
|
let arg_tt = loc.kind.arg(db)?;
|
2020-12-03 17:38:05 +00:00
|
|
|
|
2022-02-21 10:51:53 +00:00
|
|
|
let macro_def = db.macro_def(loc.def).ok()?;
|
|
|
|
let (parse, exp_map) = db.parse_macro_expansion(macro_file).value?;
|
|
|
|
let macro_arg = db.macro_arg(macro_file.macro_call_id)?;
|
|
|
|
|
2021-03-19 18:56:13 +00:00
|
|
|
let def = loc.def.ast_id().left().and_then(|id| {
|
2020-12-15 17:43:19 +00:00
|
|
|
let def_tt = match id.to_node(db) {
|
|
|
|
ast::Macro::MacroRules(mac) => mac.token_tree()?,
|
2022-02-21 10:51:53 +00:00
|
|
|
ast::Macro::MacroDef(_)
|
|
|
|
if matches!(*macro_def, TokenExpander::BuiltinAttr(_)) =>
|
|
|
|
{
|
|
|
|
return None
|
|
|
|
}
|
2021-03-27 05:44:54 +00:00
|
|
|
ast::Macro::MacroDef(mac) => mac.body()?,
|
2020-12-15 17:43:19 +00:00
|
|
|
};
|
2020-12-03 17:38:05 +00:00
|
|
|
Some(InFile::new(id.file_id, def_tt))
|
|
|
|
});
|
2021-08-22 17:12:45 +00:00
|
|
|
let attr_input_or_mac_def = def.or_else(|| match loc.kind {
|
2021-08-21 16:06:03 +00:00
|
|
|
MacroCallKind::Attr { ast_id, invoc_attr_index, .. } => {
|
|
|
|
let tt = ast_id
|
|
|
|
.to_node(db)
|
2022-01-07 17:51:10 +00:00
|
|
|
.doc_comments_and_attrs()
|
|
|
|
.nth(invoc_attr_index as usize)
|
2022-01-30 21:18:32 +00:00
|
|
|
.and_then(Either::left)?
|
2021-08-22 17:12:45 +00:00
|
|
|
.token_tree()?;
|
2021-08-21 16:06:03 +00:00
|
|
|
Some(InFile::new(ast_id.file_id, tt))
|
|
|
|
}
|
|
|
|
_ => None,
|
|
|
|
});
|
2019-11-08 20:00:27 +00:00
|
|
|
|
2019-11-17 17:15:55 +00:00
|
|
|
Some(ExpansionInfo {
|
2019-11-28 09:50:26 +00:00
|
|
|
expanded: InFile::new(self, parse.syntax_node()),
|
2019-12-05 14:10:33 +00:00
|
|
|
arg: InFile::new(loc.kind.file_id(), arg_tt),
|
2021-08-22 17:12:45 +00:00
|
|
|
attr_input_or_mac_def,
|
2021-08-21 16:06:03 +00:00
|
|
|
macro_arg_shift: mbe::Shift::new(¯o_arg.0),
|
2019-11-17 17:15:55 +00:00
|
|
|
macro_arg,
|
|
|
|
macro_def,
|
|
|
|
exp_map,
|
|
|
|
})
|
2019-11-03 17:44:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-01-12 10:08:53 +00:00
|
|
|
|
|
|
|
/// Indicate it is macro file generated for builtin derive
|
2022-02-21 12:34:05 +00:00
|
|
|
pub fn is_builtin_derive(&self, db: &dyn db::AstDatabase) -> Option<InFile<ast::Attr>> {
|
2020-01-12 10:08:53 +00:00
|
|
|
match self.0 {
|
|
|
|
HirFileIdRepr::FileId(_) => None,
|
|
|
|
HirFileIdRepr::MacroFile(macro_file) => {
|
2021-11-14 15:25:40 +00:00
|
|
|
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
|
2022-02-21 12:34:05 +00:00
|
|
|
let attr = match loc.def.kind {
|
internal: move diagnostics to hir
The idea here is to eventually get rid of `dyn Diagnostic` and
`DiagnosticSink` infrastructure altogether, and just have a `enum
hir::Diagnostic` instead.
The problem with `dyn Diagnostic` is that it is defined in the lowest
level of the stack (hir_expand), but is used by the highest level (ide).
As a first step, we free hir_expand and hir_def from `dyn Diagnostic`
and kick the can up to `hir_ty`, as an intermediate state. The plan is
then to move DiagnosticSink similarly to the hir crate, and, as final
third step, remove its usage from the ide.
One currently unsolved problem is testing. You can notice that the test
which checks precise diagnostic ranges, unresolved_import_in_use_tree,
was moved to the ide layer. Logically, only IDE should have the infra to
render a specific range.
At the same time, the range is determined with the data produced in
hir_def and hir crates, so this layering is rather unfortunate. Working
on hir_def shouldn't require compiling `ide` for testing.
2021-05-23 20:31:59 +00:00
|
|
|
MacroDefKind::BuiltInDerive(..) => loc.kind.to_node(db),
|
2020-01-12 10:08:53 +00:00
|
|
|
_ => return None,
|
|
|
|
};
|
2022-02-21 12:34:05 +00:00
|
|
|
Some(attr.with_value(ast::Attr::cast(attr.value.clone())?))
|
2020-01-12 10:08:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-03-21 15:02:01 +00:00
|
|
|
|
2021-10-10 13:50:28 +00:00
|
|
|
pub fn is_custom_derive(&self, db: &dyn db::AstDatabase) -> bool {
|
|
|
|
match self.0 {
|
|
|
|
HirFileIdRepr::FileId(_) => false,
|
|
|
|
HirFileIdRepr::MacroFile(macro_file) => {
|
2021-11-14 15:25:40 +00:00
|
|
|
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
|
2022-02-04 01:50:33 +00:00
|
|
|
matches!(loc.def.kind, MacroDefKind::ProcMacro(_, ProcMacroKind::CustomDerive, _))
|
2021-10-10 13:50:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-21 15:02:01 +00:00
|
|
|
/// Return whether this file is an include macro
|
|
|
|
pub fn is_include_macro(&self, db: &dyn db::AstDatabase) -> bool {
|
|
|
|
match self.0 {
|
2021-05-19 18:19:08 +00:00
|
|
|
HirFileIdRepr::MacroFile(macro_file) => {
|
2021-11-14 15:25:40 +00:00
|
|
|
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
|
2021-05-19 18:19:08 +00:00
|
|
|
matches!(loc.eager, Some(EagerCallInfo { included_file: Some(_), .. }))
|
|
|
|
}
|
|
|
|
_ => false,
|
2021-03-21 15:02:01 +00:00
|
|
|
}
|
|
|
|
}
|
2021-08-04 16:02:45 +00:00
|
|
|
|
2022-02-21 10:51:53 +00:00
|
|
|
/// Return whether this file is an attr macro
|
2021-09-14 12:41:38 +00:00
|
|
|
pub fn is_attr_macro(&self, db: &dyn db::AstDatabase) -> bool {
|
|
|
|
match self.0 {
|
|
|
|
HirFileIdRepr::MacroFile(macro_file) => {
|
2021-11-14 15:25:40 +00:00
|
|
|
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
|
2021-09-14 12:41:38 +00:00
|
|
|
matches!(loc.kind, MacroCallKind::Attr { .. })
|
|
|
|
}
|
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-21 10:51:53 +00:00
|
|
|
/// Return whether this file is the pseudo expansion of the derive attribute.
|
2022-02-22 11:32:15 +00:00
|
|
|
/// See [`crate::builtin_attr_macro::derive_attr_expand`].
|
|
|
|
pub fn is_derive_attr_pseudo_expansion(&self, db: &dyn db::AstDatabase) -> bool {
|
2022-02-21 10:51:53 +00:00
|
|
|
match self.0 {
|
|
|
|
HirFileIdRepr::MacroFile(macro_file) => {
|
|
|
|
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
|
|
|
|
matches!(loc.kind, MacroCallKind::Attr { is_derive: true, .. })
|
|
|
|
}
|
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-04 16:02:45 +00:00
|
|
|
pub fn is_macro(self) -> bool {
|
|
|
|
matches!(self.0, HirFileIdRepr::MacroFile(_))
|
|
|
|
}
|
2021-11-22 16:58:36 +00:00
|
|
|
|
|
|
|
pub fn macro_file(self) -> Option<MacroFile> {
|
|
|
|
match self.0 {
|
|
|
|
HirFileIdRepr::FileId(_) => None,
|
|
|
|
HirFileIdRepr::MacroFile(m) => Some(m),
|
|
|
|
}
|
|
|
|
}
|
2019-10-29 12:11:42 +00:00
|
|
|
}
|
|
|
|
|
2019-11-26 17:33:08 +00:00
|
|
|
impl MacroDefId {
|
2020-06-11 10:08:24 +00:00
|
|
|
pub fn as_lazy_macro(
|
|
|
|
self,
|
|
|
|
db: &dyn db::AstDatabase,
|
|
|
|
krate: CrateId,
|
|
|
|
kind: MacroCallKind,
|
2021-05-19 18:19:08 +00:00
|
|
|
) -> MacroCallId {
|
2021-11-14 15:25:40 +00:00
|
|
|
db.intern_macro_call(MacroCallLoc { def: self, krate, eager: None, kind })
|
2019-11-26 17:33:08 +00:00
|
|
|
}
|
2021-03-18 14:37:14 +00:00
|
|
|
|
2021-03-19 18:56:13 +00:00
|
|
|
pub fn ast_id(&self) -> Either<AstId<ast::Macro>, AstId<ast::Fn>> {
|
2022-02-04 01:50:33 +00:00
|
|
|
let id = match self.kind {
|
|
|
|
MacroDefKind::ProcMacro(.., id) => return Either::Right(id),
|
2021-08-19 20:53:00 +00:00
|
|
|
MacroDefKind::Declarative(id)
|
|
|
|
| MacroDefKind::BuiltIn(_, id)
|
|
|
|
| MacroDefKind::BuiltInAttr(_, id)
|
|
|
|
| MacroDefKind::BuiltInDerive(_, id)
|
|
|
|
| MacroDefKind::BuiltInEager(_, id) => id,
|
2021-03-18 14:37:14 +00:00
|
|
|
};
|
2022-02-04 01:50:33 +00:00
|
|
|
Either::Left(id)
|
2021-03-18 14:37:14 +00:00
|
|
|
}
|
2021-05-11 22:27:16 +00:00
|
|
|
|
|
|
|
pub fn is_proc_macro(&self) -> bool {
|
|
|
|
matches!(self.kind, MacroDefKind::ProcMacro(..))
|
|
|
|
}
|
2021-10-21 10:21:34 +00:00
|
|
|
|
|
|
|
pub fn is_attribute(&self) -> bool {
|
|
|
|
matches!(
|
|
|
|
self.kind,
|
|
|
|
MacroDefKind::BuiltInAttr(..) | MacroDefKind::ProcMacro(_, ProcMacroKind::Attr, _)
|
|
|
|
)
|
|
|
|
}
|
2019-11-26 17:33:08 +00:00
|
|
|
}
|
|
|
|
|
2021-05-31 11:37:11 +00:00
|
|
|
// FIXME: attribute indices do not account for `cfg_attr`, which means that we'll strip the whole
|
|
|
|
// `cfg_attr` instead of just one of the attributes it expands to
|
|
|
|
|
2019-12-05 14:10:33 +00:00
|
|
|
impl MacroCallKind {
|
2021-05-19 18:19:08 +00:00
|
|
|
/// Returns the file containing the macro invocation.
|
2020-07-23 13:07:01 +00:00
|
|
|
fn file_id(&self) -> HirFileId {
|
2022-01-07 13:19:11 +00:00
|
|
|
match *self {
|
|
|
|
MacroCallKind::FnLike { ast_id: InFile { file_id, .. }, .. }
|
|
|
|
| MacroCallKind::Derive { ast_id: InFile { file_id, .. }, .. }
|
|
|
|
| MacroCallKind::Attr { ast_id: InFile { file_id, .. }, .. } => file_id,
|
2019-12-05 14:10:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
internal: move diagnostics to hir
The idea here is to eventually get rid of `dyn Diagnostic` and
`DiagnosticSink` infrastructure altogether, and just have a `enum
hir::Diagnostic` instead.
The problem with `dyn Diagnostic` is that it is defined in the lowest
level of the stack (hir_expand), but is used by the highest level (ide).
As a first step, we free hir_expand and hir_def from `dyn Diagnostic`
and kick the can up to `hir_ty`, as an intermediate state. The plan is
then to move DiagnosticSink similarly to the hir crate, and, as final
third step, remove its usage from the ide.
One currently unsolved problem is testing. You can notice that the test
which checks precise diagnostic ranges, unresolved_import_in_use_tree,
was moved to the ide layer. Logically, only IDE should have the infra to
render a specific range.
At the same time, the range is determined with the data produced in
hir_def and hir crates, so this layering is rather unfortunate. Working
on hir_def shouldn't require compiling `ide` for testing.
2021-05-23 20:31:59 +00:00
|
|
|
pub fn to_node(&self, db: &dyn db::AstDatabase) -> InFile<SyntaxNode> {
|
2019-12-06 18:30:15 +00:00
|
|
|
match self {
|
2021-04-08 18:43:07 +00:00
|
|
|
MacroCallKind::FnLike { ast_id, .. } => {
|
|
|
|
ast_id.with_value(ast_id.to_node(db).syntax().clone())
|
|
|
|
}
|
2022-02-21 12:34:05 +00:00
|
|
|
MacroCallKind::Derive { ast_id, derive_attr_index, .. } => {
|
|
|
|
// FIXME: handle `cfg_attr`
|
|
|
|
ast_id.with_value(ast_id.to_node(db)).map(|it| {
|
|
|
|
it.doc_comments_and_attrs()
|
|
|
|
.nth(*derive_attr_index as usize)
|
|
|
|
.and_then(|it| match it {
|
|
|
|
Either::Left(attr) => Some(attr.syntax().clone()),
|
|
|
|
Either::Right(_) => None,
|
|
|
|
})
|
|
|
|
.unwrap_or_else(|| it.syntax().clone())
|
|
|
|
})
|
2022-01-07 13:19:11 +00:00
|
|
|
}
|
2022-02-21 11:57:57 +00:00
|
|
|
MacroCallKind::Attr { ast_id, is_derive: true, invoc_attr_index, .. } => {
|
2022-02-21 12:34:05 +00:00
|
|
|
// FIXME: handle `cfg_attr`
|
2022-02-21 11:57:57 +00:00
|
|
|
ast_id.with_value(ast_id.to_node(db)).map(|it| {
|
|
|
|
it.doc_comments_and_attrs()
|
|
|
|
.nth(*invoc_attr_index as usize)
|
|
|
|
.and_then(|it| match it {
|
|
|
|
Either::Left(attr) => Some(attr.syntax().clone()),
|
|
|
|
Either::Right(_) => None,
|
|
|
|
})
|
|
|
|
.unwrap_or_else(|| it.syntax().clone())
|
|
|
|
})
|
|
|
|
}
|
2022-01-07 13:19:11 +00:00
|
|
|
MacroCallKind::Attr { ast_id, .. } => {
|
2020-03-18 09:47:59 +00:00
|
|
|
ast_id.with_value(ast_id.to_node(db).syntax().clone())
|
|
|
|
}
|
2019-12-06 18:30:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-23 14:36:11 +00:00
|
|
|
/// Returns the original file range that best describes the location of this macro call.
|
|
|
|
///
|
|
|
|
/// Unlike `MacroCallKind::original_call_range`, this also spans the item of attributes and derives.
|
|
|
|
pub fn original_call_range_with_body(self, db: &dyn db::AstDatabase) -> FileRange {
|
|
|
|
let mut kind = self;
|
|
|
|
let file_id = loop {
|
|
|
|
match kind.file_id().0 {
|
|
|
|
HirFileIdRepr::MacroFile(file) => {
|
|
|
|
kind = db.lookup_intern_macro_call(file.macro_call_id).kind;
|
|
|
|
}
|
|
|
|
HirFileIdRepr::FileId(file_id) => break file_id,
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
let range = match kind {
|
|
|
|
MacroCallKind::FnLike { ast_id, .. } => ast_id.to_node(db).syntax().text_range(),
|
|
|
|
MacroCallKind::Derive { ast_id, .. } => ast_id.to_node(db).syntax().text_range(),
|
|
|
|
MacroCallKind::Attr { ast_id, .. } => ast_id.to_node(db).syntax().text_range(),
|
|
|
|
};
|
|
|
|
|
|
|
|
FileRange { range, file_id }
|
|
|
|
}
|
|
|
|
|
2022-01-08 14:38:50 +00:00
|
|
|
/// Returns the original file range that best describes the location of this macro call.
|
|
|
|
///
|
|
|
|
/// Here we try to roughly match what rustc does to improve diagnostics: fn-like macros
|
|
|
|
/// get the whole `ast::MacroCall`, attribute macros get the attribute's range, and derives
|
|
|
|
/// get only the specific derive that is being referred to.
|
|
|
|
pub fn original_call_range(self, db: &dyn db::AstDatabase) -> FileRange {
|
|
|
|
let mut kind = self;
|
2022-02-04 01:50:33 +00:00
|
|
|
let file_id = loop {
|
2022-01-08 14:38:50 +00:00
|
|
|
match kind.file_id().0 {
|
|
|
|
HirFileIdRepr::MacroFile(file) => {
|
|
|
|
kind = db.lookup_intern_macro_call(file.macro_call_id).kind;
|
|
|
|
}
|
2022-02-04 01:50:33 +00:00
|
|
|
HirFileIdRepr::FileId(file_id) => break file_id,
|
2022-01-08 14:38:50 +00:00
|
|
|
}
|
|
|
|
};
|
2022-02-04 01:50:33 +00:00
|
|
|
|
2022-01-08 14:38:50 +00:00
|
|
|
let range = match kind {
|
|
|
|
MacroCallKind::FnLike { ast_id, .. } => ast_id.to_node(db).syntax().text_range(),
|
|
|
|
MacroCallKind::Derive { ast_id, derive_attr_index, .. } => {
|
|
|
|
// FIXME: should be the range of the macro name, not the whole derive
|
|
|
|
ast_id
|
|
|
|
.to_node(db)
|
|
|
|
.doc_comments_and_attrs()
|
|
|
|
.nth(derive_attr_index as usize)
|
|
|
|
.expect("missing derive")
|
2022-01-30 21:18:32 +00:00
|
|
|
.expect_left("derive is a doc comment?")
|
2022-01-08 14:38:50 +00:00
|
|
|
.syntax()
|
|
|
|
.text_range()
|
|
|
|
}
|
|
|
|
MacroCallKind::Attr { ast_id, invoc_attr_index, .. } => ast_id
|
|
|
|
.to_node(db)
|
|
|
|
.doc_comments_and_attrs()
|
|
|
|
.nth(invoc_attr_index as usize)
|
|
|
|
.expect("missing attribute")
|
2022-01-30 21:18:32 +00:00
|
|
|
.expect_left("attribute macro is a doc comment?")
|
2022-01-08 14:38:50 +00:00
|
|
|
.syntax()
|
|
|
|
.text_range(),
|
|
|
|
};
|
|
|
|
|
|
|
|
FileRange { range, file_id }
|
|
|
|
}
|
|
|
|
|
2020-07-23 13:07:01 +00:00
|
|
|
fn arg(&self, db: &dyn db::AstDatabase) -> Option<SyntaxNode> {
|
2019-12-05 14:10:33 +00:00
|
|
|
match self {
|
2021-04-08 18:43:07 +00:00
|
|
|
MacroCallKind::FnLike { ast_id, .. } => {
|
2019-12-05 14:10:33 +00:00
|
|
|
Some(ast_id.to_node(db).token_tree()?.syntax().clone())
|
|
|
|
}
|
2022-01-07 13:19:11 +00:00
|
|
|
MacroCallKind::Derive { ast_id, .. } => Some(ast_id.to_node(db).syntax().clone()),
|
|
|
|
MacroCallKind::Attr { ast_id, .. } => Some(ast_id.to_node(db).syntax().clone()),
|
2019-12-05 14:10:33 +00:00
|
|
|
}
|
|
|
|
}
|
2021-05-08 23:36:06 +00:00
|
|
|
|
2021-09-05 19:30:06 +00:00
|
|
|
fn expand_to(&self) -> ExpandTo {
|
2021-05-08 23:36:06 +00:00
|
|
|
match self {
|
2021-09-05 19:30:06 +00:00
|
|
|
MacroCallKind::FnLike { expand_to, .. } => *expand_to,
|
|
|
|
MacroCallKind::Derive { .. } => ExpandTo::Items,
|
2022-02-21 01:42:58 +00:00
|
|
|
MacroCallKind::Attr { is_derive: true, .. } => ExpandTo::Statements,
|
2021-09-05 19:30:06 +00:00
|
|
|
MacroCallKind::Attr { .. } => ExpandTo::Items, // is this always correct?
|
2021-05-08 23:36:06 +00:00
|
|
|
}
|
|
|
|
}
|
2019-10-29 12:11:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl MacroCallId {
|
2019-12-08 08:16:52 +00:00
|
|
|
pub fn as_file(self) -> HirFileId {
|
|
|
|
MacroFile { macro_call_id: self }.into()
|
2019-10-29 12:11:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-03 19:12:19 +00:00
|
|
|
/// ExpansionInfo mainly describes how to map text range between src and expanded macro
|
2019-11-17 17:15:55 +00:00
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
2019-11-03 14:46:12 +00:00
|
|
|
pub struct ExpansionInfo {
|
2019-11-28 09:50:26 +00:00
|
|
|
expanded: InFile<SyntaxNode>,
|
2021-11-22 14:59:41 +00:00
|
|
|
/// The argument TokenTree or item for attributes
|
2019-12-05 14:10:33 +00:00
|
|
|
arg: InFile<SyntaxNode>,
|
2021-11-22 14:59:41 +00:00
|
|
|
/// The `macro_rules!` or attribute input.
|
2021-08-21 16:06:03 +00:00
|
|
|
attr_input_or_mac_def: Option<InFile<ast::TokenTree>>,
|
2019-11-08 20:00:27 +00:00
|
|
|
|
2021-08-21 16:06:03 +00:00
|
|
|
macro_def: Arc<TokenExpander>,
|
2022-02-09 15:30:10 +00:00
|
|
|
macro_arg: Arc<(tt::Subtree, mbe::TokenMap, fixup::SyntaxFixupUndoInfo)>,
|
2021-11-22 16:58:36 +00:00
|
|
|
/// A shift built from `macro_arg`'s subtree, relevant for attributes as the item is the macro arg
|
|
|
|
/// and as such we need to shift tokens if they are part of an attributes input instead of their item.
|
2021-08-21 16:06:03 +00:00
|
|
|
macro_arg_shift: mbe::Shift,
|
2019-11-18 13:08:41 +00:00
|
|
|
exp_map: Arc<mbe::TokenMap>,
|
2019-11-03 14:46:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl ExpansionInfo {
|
2021-11-05 14:52:10 +00:00
|
|
|
pub fn expanded(&self) -> InFile<SyntaxNode> {
|
|
|
|
self.expanded.clone()
|
|
|
|
}
|
|
|
|
|
2019-12-14 17:20:07 +00:00
|
|
|
pub fn call_node(&self) -> Option<InFile<SyntaxNode>> {
|
|
|
|
Some(self.arg.with_value(self.arg.value.parent()?))
|
|
|
|
}
|
|
|
|
|
2021-11-22 16:58:36 +00:00
|
|
|
/// Map a token down from macro input into the macro expansion.
|
|
|
|
///
|
|
|
|
/// The inner workings of this function differ slightly depending on the type of macro we are dealing with:
|
|
|
|
/// - declarative:
|
|
|
|
/// For declarative macros, we need to accommodate for the macro definition site(which acts as a second unchanging input)
|
|
|
|
/// , as tokens can mapped in and out of it.
|
|
|
|
/// To do this we shift all ids in the expansion by the maximum id of the definition site giving us an easy
|
|
|
|
/// way to map all the tokens.
|
|
|
|
/// - attribute:
|
|
|
|
/// Attributes have two different inputs, the input tokentree in the attribute node and the item
|
|
|
|
/// the attribute is annotating. Similarly as for declarative macros we need to do a shift here
|
|
|
|
/// as well. Currently this is done by shifting the attribute input by the maximum id of the item.
|
|
|
|
/// - function-like and derives:
|
|
|
|
/// Both of these only have one simple call site input so no special handling is required here.
|
2021-08-21 16:06:03 +00:00
|
|
|
pub fn map_token_down(
|
|
|
|
&self,
|
|
|
|
db: &dyn db::AstDatabase,
|
|
|
|
item: Option<ast::Item>,
|
|
|
|
token: InFile<&SyntaxToken>,
|
2021-08-28 19:18:56 +00:00
|
|
|
) -> Option<impl Iterator<Item = InFile<SyntaxToken>> + '_> {
|
2019-11-18 12:08:39 +00:00
|
|
|
assert_eq!(token.file_id, self.arg.file_id);
|
2021-11-22 16:58:36 +00:00
|
|
|
let token_id_in_attr_input = if let Some(item) = item {
|
2021-09-02 16:54:09 +00:00
|
|
|
// check if we are mapping down in an attribute input
|
2021-11-22 16:58:36 +00:00
|
|
|
// this is a special case as attributes can have two inputs
|
|
|
|
let call_id = self.expanded.file_id.macro_file()?.macro_call_id;
|
2021-11-14 15:25:40 +00:00
|
|
|
let loc = db.lookup_intern_macro_call(call_id);
|
2021-08-21 16:06:03 +00:00
|
|
|
|
|
|
|
let token_range = token.value.text_range();
|
|
|
|
match &loc.kind {
|
2022-02-21 01:42:58 +00:00
|
|
|
MacroCallKind::Attr { attr_args, invoc_attr_index, is_derive, .. } => {
|
2022-01-07 17:51:10 +00:00
|
|
|
let attr = item
|
|
|
|
.doc_comments_and_attrs()
|
|
|
|
.nth(*invoc_attr_index as usize)
|
2022-01-30 21:18:32 +00:00
|
|
|
.and_then(Either::left)?;
|
2021-08-21 16:06:03 +00:00
|
|
|
match attr.token_tree() {
|
|
|
|
Some(token_tree)
|
|
|
|
if token_tree.syntax().text_range().contains_range(token_range) =>
|
|
|
|
{
|
|
|
|
let attr_input_start =
|
|
|
|
token_tree.left_delimiter_token()?.text_range().start();
|
2021-11-22 16:58:36 +00:00
|
|
|
let relative_range =
|
|
|
|
token.value.text_range().checked_sub(attr_input_start)?;
|
|
|
|
// shift by the item's tree's max id
|
2022-02-21 01:42:58 +00:00
|
|
|
let token_id = attr_args.1.token_by_range(relative_range)?;
|
|
|
|
let token_id = if *is_derive {
|
|
|
|
// we do not shift for `#[derive]`, as we only need to downmap the derive attribute tokens
|
|
|
|
token_id
|
|
|
|
} else {
|
|
|
|
self.macro_arg_shift.shift(token_id)
|
|
|
|
};
|
2021-08-21 16:06:03 +00:00
|
|
|
Some(token_id)
|
|
|
|
}
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
|
|
|
|
2021-11-22 16:58:36 +00:00
|
|
|
let token_id = match token_id_in_attr_input {
|
2021-08-21 16:06:03 +00:00
|
|
|
Some(token_id) => token_id,
|
2022-08-17 13:44:58 +00:00
|
|
|
// the token is not inside an attribute's input so do the lookup in the macro_arg as usual
|
2021-08-21 16:06:03 +00:00
|
|
|
None => {
|
2021-11-22 16:58:36 +00:00
|
|
|
let relative_range =
|
2021-08-21 16:06:03 +00:00
|
|
|
token.value.text_range().checked_sub(self.arg.value.text_range().start())?;
|
2021-11-22 16:58:36 +00:00
|
|
|
let token_id = self.macro_arg.1.token_by_range(relative_range)?;
|
|
|
|
// conditionally shift the id by a declaratives macro definition
|
2021-08-21 16:06:03 +00:00
|
|
|
self.macro_def.map_id_down(token_id)
|
|
|
|
}
|
|
|
|
};
|
2019-11-16 13:49:26 +00:00
|
|
|
|
2021-08-28 19:18:56 +00:00
|
|
|
let tokens = self
|
|
|
|
.exp_map
|
|
|
|
.ranges_by_token(token_id, token.value.kind())
|
|
|
|
.flat_map(move |range| self.expanded.value.covering_element(range).into_token());
|
2019-11-17 17:15:55 +00:00
|
|
|
|
2021-08-28 19:18:56 +00:00
|
|
|
Some(tokens.map(move |token| self.expanded.with_value(token)))
|
2019-11-16 13:49:26 +00:00
|
|
|
}
|
|
|
|
|
2021-11-22 16:58:36 +00:00
|
|
|
/// Map a token up out of the expansion it resides in into the arguments of the macro call of the expansion.
|
2021-01-03 10:47:57 +00:00
|
|
|
pub fn map_token_up(
|
|
|
|
&self,
|
2021-08-21 16:06:03 +00:00
|
|
|
db: &dyn db::AstDatabase,
|
2021-01-03 10:47:57 +00:00
|
|
|
token: InFile<&SyntaxToken>,
|
|
|
|
) -> Option<(InFile<SyntaxToken>, Origin)> {
|
2021-11-22 16:58:36 +00:00
|
|
|
// Fetch the id through its text range,
|
2021-01-03 10:47:57 +00:00
|
|
|
let token_id = self.exp_map.token_by_range(token.value.text_range())?;
|
2021-11-22 16:58:36 +00:00
|
|
|
// conditionally unshifting the id to accommodate for macro-rules def site
|
2021-08-21 16:06:03 +00:00
|
|
|
let (mut token_id, origin) = self.macro_def.map_id_up(token_id);
|
2019-11-09 09:49:35 +00:00
|
|
|
|
2021-11-22 16:58:36 +00:00
|
|
|
let call_id = self.expanded.file_id.macro_file()?.macro_call_id;
|
2021-11-14 15:25:40 +00:00
|
|
|
let loc = db.lookup_intern_macro_call(call_id);
|
2021-08-21 16:06:03 +00:00
|
|
|
|
2021-11-22 16:58:36 +00:00
|
|
|
// Attributes are a bit special for us, they have two inputs, the input tokentree and the annotated item.
|
2021-08-21 16:06:03 +00:00
|
|
|
let (token_map, tt) = match &loc.kind {
|
2022-02-21 10:51:53 +00:00
|
|
|
MacroCallKind::Attr { attr_args, is_derive: true, .. } => {
|
|
|
|
(&attr_args.1, self.attr_input_or_mac_def.clone()?.syntax().cloned())
|
|
|
|
}
|
2022-02-20 21:53:04 +00:00
|
|
|
MacroCallKind::Attr { attr_args, .. } => {
|
2021-11-22 16:58:36 +00:00
|
|
|
// try unshifting the the token id, if unshifting fails, the token resides in the non-item attribute input
|
|
|
|
// note that the `TokenExpander::map_id_up` earlier only unshifts for declarative macros, so we don't double unshift with this
|
|
|
|
match self.macro_arg_shift.unshift(token_id) {
|
|
|
|
Some(unshifted) => {
|
|
|
|
token_id = unshifted;
|
2022-02-20 21:53:04 +00:00
|
|
|
(&attr_args.1, self.attr_input_or_mac_def.clone()?.syntax().cloned())
|
2021-11-22 16:58:36 +00:00
|
|
|
}
|
|
|
|
None => (&self.macro_arg.1, self.arg.clone()),
|
2021-08-21 16:06:03 +00:00
|
|
|
}
|
2021-11-22 16:58:36 +00:00
|
|
|
}
|
2021-08-21 16:06:03 +00:00
|
|
|
_ => match origin {
|
|
|
|
mbe::Origin::Call => (&self.macro_arg.1, self.arg.clone()),
|
2021-08-22 17:12:45 +00:00
|
|
|
mbe::Origin::Def => match (&*self.macro_def, &self.attr_input_or_mac_def) {
|
2021-10-10 18:07:43 +00:00
|
|
|
(TokenExpander::DeclarativeMacro { def_site_token_map, .. }, Some(tt)) => {
|
|
|
|
(def_site_token_map, tt.syntax().cloned())
|
|
|
|
}
|
2021-08-21 16:06:03 +00:00
|
|
|
_ => panic!("`Origin::Def` used with non-`macro_rules!` macro"),
|
|
|
|
},
|
2021-05-04 19:03:16 +00:00
|
|
|
},
|
2019-11-09 04:00:46 +00:00
|
|
|
};
|
2019-11-03 14:46:12 +00:00
|
|
|
|
2021-08-28 19:18:56 +00:00
|
|
|
let range = token_map.first_range_by_token(token_id, token.value.kind())?;
|
2021-01-15 17:15:33 +00:00
|
|
|
let token =
|
|
|
|
tt.value.covering_element(range + tt.value.text_range().start()).into_token()?;
|
2019-12-14 17:20:07 +00:00
|
|
|
Some((tt.with_value(token), origin))
|
2019-11-03 14:46:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-29 12:11:42 +00:00
|
|
|
/// `AstId` points to an AST node in any file.
|
|
|
|
///
|
|
|
|
/// It is stable across reparses, and can be used as salsa key/value.
|
2019-11-28 13:00:03 +00:00
|
|
|
pub type AstId<N> = InFile<FileAstId<N>>;
|
2019-10-29 12:11:42 +00:00
|
|
|
|
|
|
|
impl<N: AstNode> AstId<N> {
|
2019-10-30 13:12:55 +00:00
|
|
|
pub fn to_node(&self, db: &dyn db::AstDatabase) -> N {
|
2019-10-29 12:22:20 +00:00
|
|
|
let root = db.parse_or_expand(self.file_id).unwrap();
|
2019-11-28 13:00:03 +00:00
|
|
|
db.ast_id_map(self.file_id).get(self.value).to_node(&root)
|
2019-10-29 12:11:42 +00:00
|
|
|
}
|
|
|
|
}
|
2019-11-02 20:11:05 +00:00
|
|
|
|
2019-11-28 09:50:26 +00:00
|
|
|
/// `InFile<T>` stores a value of `T` inside a particular file/syntax tree.
|
2019-11-20 06:40:36 +00:00
|
|
|
///
|
|
|
|
/// Typical usages are:
|
|
|
|
///
|
2019-11-28 09:50:26 +00:00
|
|
|
/// * `InFile<SyntaxNode>` -- syntax node in a file
|
|
|
|
/// * `InFile<ast::FnDef>` -- ast node in a file
|
2020-04-24 21:40:41 +00:00
|
|
|
/// * `InFile<TextSize>` -- offset in a file
|
2019-11-14 07:30:30 +00:00
|
|
|
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
|
2019-11-28 09:50:26 +00:00
|
|
|
pub struct InFile<T> {
|
2019-11-02 20:11:05 +00:00
|
|
|
pub file_id: HirFileId,
|
2019-11-20 06:40:36 +00:00
|
|
|
pub value: T,
|
2019-11-02 20:11:05 +00:00
|
|
|
}
|
|
|
|
|
2019-11-28 09:50:26 +00:00
|
|
|
impl<T> InFile<T> {
|
|
|
|
pub fn new(file_id: HirFileId, value: T) -> InFile<T> {
|
|
|
|
InFile { file_id, value }
|
2019-11-15 20:24:56 +00:00
|
|
|
}
|
|
|
|
|
2019-11-28 09:50:26 +00:00
|
|
|
pub fn with_value<U>(&self, value: U) -> InFile<U> {
|
|
|
|
InFile::new(self.file_id, value)
|
2019-11-15 21:40:54 +00:00
|
|
|
}
|
|
|
|
|
2019-11-28 09:50:26 +00:00
|
|
|
pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> InFile<U> {
|
|
|
|
InFile::new(self.file_id, f(self.value))
|
2019-11-02 20:11:05 +00:00
|
|
|
}
|
2022-04-06 11:58:40 +00:00
|
|
|
|
2019-11-28 09:50:26 +00:00
|
|
|
pub fn as_ref(&self) -> InFile<&T> {
|
2019-11-20 10:09:21 +00:00
|
|
|
self.with_value(&self.value)
|
2019-11-14 07:30:30 +00:00
|
|
|
}
|
2022-04-06 11:58:40 +00:00
|
|
|
|
2020-03-13 15:05:46 +00:00
|
|
|
pub fn file_syntax(&self, db: &dyn db::AstDatabase) -> SyntaxNode {
|
2019-11-02 20:11:05 +00:00
|
|
|
db.parse_or_expand(self.file_id).expect("source created from invalid file")
|
|
|
|
}
|
|
|
|
}
|
2019-12-06 20:46:18 +00:00
|
|
|
|
|
|
|
impl<T: Clone> InFile<&T> {
|
|
|
|
pub fn cloned(&self) -> InFile<T> {
|
|
|
|
self.with_value(self.value.clone())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-12 17:19:55 +00:00
|
|
|
impl<T> InFile<Option<T>> {
|
|
|
|
pub fn transpose(self) -> Option<InFile<T>> {
|
|
|
|
let value = self.value?;
|
|
|
|
Some(InFile::new(self.file_id, value))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-21 12:38:58 +00:00
|
|
|
impl<'a> InFile<&'a SyntaxNode> {
|
2020-02-12 17:19:55 +00:00
|
|
|
pub fn ancestors_with_macros(
|
2019-12-06 20:46:18 +00:00
|
|
|
self,
|
2020-03-13 15:05:46 +00:00
|
|
|
db: &dyn db::AstDatabase,
|
2021-09-14 12:09:52 +00:00
|
|
|
) -> impl Iterator<Item = InFile<SyntaxNode>> + Clone + '_ {
|
2021-12-21 12:38:58 +00:00
|
|
|
iter::successors(Some(self.cloned()), move |node| match node.value.parent() {
|
2019-12-06 20:46:18 +00:00
|
|
|
Some(parent) => Some(node.with_value(parent)),
|
2022-01-14 10:07:53 +00:00
|
|
|
None => node.file_id.call_node(db),
|
2019-12-06 20:46:18 +00:00
|
|
|
})
|
|
|
|
}
|
2021-09-14 12:41:38 +00:00
|
|
|
|
|
|
|
/// Skips the attributed item that caused the macro invocation we are climbing up
|
|
|
|
pub fn ancestors_with_macros_skip_attr_item(
|
|
|
|
self,
|
|
|
|
db: &dyn db::AstDatabase,
|
|
|
|
) -> impl Iterator<Item = InFile<SyntaxNode>> + '_ {
|
2022-03-06 16:56:02 +00:00
|
|
|
let succ = move |node: &InFile<SyntaxNode>| match node.value.parent() {
|
2021-09-14 12:41:38 +00:00
|
|
|
Some(parent) => Some(node.with_value(parent)),
|
|
|
|
None => {
|
|
|
|
let parent_node = node.file_id.call_node(db)?;
|
|
|
|
if node.file_id.is_attr_macro(db) {
|
|
|
|
// macro call was an attributed item, skip it
|
|
|
|
// FIXME: does this fail if this is a direct expansion of another macro?
|
|
|
|
parent_node.map(|node| node.parent()).transpose()
|
|
|
|
} else {
|
|
|
|
Some(parent_node)
|
|
|
|
}
|
|
|
|
}
|
2022-03-06 16:56:02 +00:00
|
|
|
};
|
|
|
|
iter::successors(succ(&self.cloned()), succ)
|
2021-09-14 12:41:38 +00:00
|
|
|
}
|
2020-01-05 20:32:18 +00:00
|
|
|
|
2021-08-19 20:53:00 +00:00
|
|
|
/// Falls back to the macro call range if the node cannot be mapped up fully.
|
2022-04-23 14:36:11 +00:00
|
|
|
///
|
|
|
|
/// For attributes and derives, this will point back to the attribute only.
|
|
|
|
/// For the entire item `InFile::use original_file_range_full`.
|
2020-12-08 18:01:27 +00:00
|
|
|
pub fn original_file_range(self, db: &dyn db::AstDatabase) -> FileRange {
|
2022-01-08 14:38:50 +00:00
|
|
|
match self.file_id.0 {
|
|
|
|
HirFileIdRepr::FileId(file_id) => FileRange { file_id, range: self.value.text_range() },
|
|
|
|
HirFileIdRepr::MacroFile(mac_file) => {
|
2022-07-05 10:46:09 +00:00
|
|
|
if let Some(res) = self.original_file_range_opt(db) {
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
// Fall back to whole macro call.
|
2022-01-08 14:38:50 +00:00
|
|
|
let loc = db.lookup_intern_macro_call(mac_file.macro_call_id);
|
|
|
|
loc.kind.original_call_range(db)
|
|
|
|
}
|
2020-12-08 18:01:27 +00:00
|
|
|
}
|
|
|
|
}
|
2021-08-19 20:53:00 +00:00
|
|
|
|
|
|
|
/// Attempts to map the syntax node back up its macro calls.
|
|
|
|
pub fn original_file_range_opt(self, db: &dyn db::AstDatabase) -> Option<FileRange> {
|
2021-11-03 20:12:36 +00:00
|
|
|
match ascend_node_border_tokens(db, self) {
|
|
|
|
Some(InFile { file_id, value: (first, last) }) => {
|
|
|
|
let original_file = file_id.original_file(db);
|
|
|
|
let range = first.text_range().cover(last.text_range());
|
|
|
|
if file_id != original_file.into() {
|
2021-08-15 12:46:13 +00:00
|
|
|
tracing::error!("Failed mapping up more for {:?}", range);
|
2021-11-03 20:12:36 +00:00
|
|
|
return None;
|
2021-08-19 20:53:00 +00:00
|
|
|
}
|
2021-11-03 20:12:36 +00:00
|
|
|
Some(FileRange { file_id: original_file, range })
|
2021-08-19 20:53:00 +00:00
|
|
|
}
|
|
|
|
_ if !self.file_id.is_macro() => Some(FileRange {
|
|
|
|
file_id: self.file_id.original_file(db),
|
|
|
|
range: self.value.text_range(),
|
|
|
|
}),
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
2022-10-01 13:34:45 +00:00
|
|
|
|
|
|
|
pub fn original_syntax_node(self, db: &dyn db::AstDatabase) -> Option<InFile<SyntaxNode>> {
|
|
|
|
// This kind of upmapping can only be achieved in attribute expanded files,
|
2022-11-07 10:53:33 +00:00
|
|
|
// as we don't have node inputs otherwise and therefore can't find an `N` node in the input
|
2022-10-01 13:34:45 +00:00
|
|
|
if !self.file_id.is_macro() {
|
|
|
|
return Some(self.map(Clone::clone));
|
|
|
|
} else if !self.file_id.is_attr_macro(db) {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(InFile { file_id, value: (first, last) }) = ascend_node_border_tokens(db, self)
|
|
|
|
{
|
|
|
|
if file_id.is_macro() {
|
|
|
|
let range = first.text_range().cover(last.text_range());
|
|
|
|
tracing::error!("Failed mapping out of macro file for {:?}", range);
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
// FIXME: This heuristic is brittle and with the right macro may select completely unrelated nodes
|
|
|
|
let anc = algo::least_common_ancestor(&first.parent()?, &last.parent()?)?;
|
|
|
|
let kind = self.value.kind();
|
|
|
|
let value = anc.ancestors().find(|it| it.kind() == kind)?;
|
|
|
|
return Some(InFile::new(file_id, value));
|
|
|
|
}
|
|
|
|
None
|
|
|
|
}
|
2020-12-08 18:01:27 +00:00
|
|
|
}
|
|
|
|
|
2022-02-21 10:51:53 +00:00
|
|
|
impl InFile<SyntaxToken> {
|
|
|
|
pub fn upmap(self, db: &dyn db::AstDatabase) -> Option<InFile<SyntaxToken>> {
|
|
|
|
let expansion = self.file_id.expansion_info(db)?;
|
|
|
|
expansion.map_token_up(db, self.as_ref()).map(|(it, _)| it)
|
|
|
|
}
|
2022-07-05 10:46:09 +00:00
|
|
|
|
|
|
|
/// Falls back to the macro call range if the node cannot be mapped up fully.
|
|
|
|
pub fn original_file_range(self, db: &dyn db::AstDatabase) -> FileRange {
|
|
|
|
match self.file_id.0 {
|
|
|
|
HirFileIdRepr::FileId(file_id) => FileRange { file_id, range: self.value.text_range() },
|
|
|
|
HirFileIdRepr::MacroFile(mac_file) => {
|
|
|
|
if let Some(res) = self.original_file_range_opt(db) {
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
// Fall back to whole macro call.
|
|
|
|
let loc = db.lookup_intern_macro_call(mac_file.macro_call_id);
|
|
|
|
loc.kind.original_call_range(db)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Attempts to map the syntax node back up its macro calls.
|
|
|
|
pub fn original_file_range_opt(self, db: &dyn db::AstDatabase) -> Option<FileRange> {
|
|
|
|
match self.file_id.0 {
|
|
|
|
HirFileIdRepr::FileId(file_id) => {
|
|
|
|
Some(FileRange { file_id, range: self.value.text_range() })
|
|
|
|
}
|
|
|
|
HirFileIdRepr::MacroFile(_) => {
|
|
|
|
let expansion = self.file_id.expansion_info(db)?;
|
|
|
|
let InFile { file_id, value } = ascend_call_token(db, &expansion, self)?;
|
|
|
|
let original_file = file_id.original_file(db);
|
|
|
|
if file_id != original_file.into() {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
Some(FileRange { file_id: original_file, range: value.text_range() })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn ancestors_with_macros(
|
|
|
|
self,
|
|
|
|
db: &dyn db::AstDatabase,
|
|
|
|
) -> impl Iterator<Item = InFile<SyntaxNode>> + '_ {
|
|
|
|
self.value.parent().into_iter().flat_map({
|
|
|
|
let file_id = self.file_id;
|
|
|
|
move |parent| InFile::new(file_id, &parent).ancestors_with_macros(db)
|
|
|
|
})
|
|
|
|
}
|
2022-02-21 10:51:53 +00:00
|
|
|
}
|
|
|
|
|
2021-11-03 20:12:36 +00:00
|
|
|
fn ascend_node_border_tokens(
|
2020-12-08 18:01:27 +00:00
|
|
|
db: &dyn db::AstDatabase,
|
2021-11-03 20:12:36 +00:00
|
|
|
InFile { file_id, value: node }: InFile<&SyntaxNode>,
|
|
|
|
) -> Option<InFile<(SyntaxToken, SyntaxToken)>> {
|
|
|
|
let expansion = file_id.expansion_info(db)?;
|
2020-12-08 18:01:27 +00:00
|
|
|
|
2022-01-31 12:10:53 +00:00
|
|
|
let first_token = |node: &SyntaxNode| skip_trivia_token(node.first_token()?, Direction::Next);
|
|
|
|
let last_token = |node: &SyntaxNode| skip_trivia_token(node.last_token()?, Direction::Prev);
|
|
|
|
|
|
|
|
let first = first_token(node)?;
|
|
|
|
let last = last_token(node)?;
|
2022-01-31 12:56:14 +00:00
|
|
|
let first = ascend_call_token(db, &expansion, InFile::new(file_id, first))?;
|
|
|
|
let last = ascend_call_token(db, &expansion, InFile::new(file_id, last))?;
|
|
|
|
(first.file_id == last.file_id).then(|| InFile::new(first.file_id, (first.value, last.value)))
|
2020-12-08 18:01:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn ascend_call_token(
|
|
|
|
db: &dyn db::AstDatabase,
|
|
|
|
expansion: &ExpansionInfo,
|
|
|
|
token: InFile<SyntaxToken>,
|
|
|
|
) -> Option<InFile<SyntaxToken>> {
|
2022-02-11 21:12:16 +00:00
|
|
|
let mut mapping = expansion.map_token_up(db, token.as_ref())?;
|
|
|
|
while let (mapped, Origin::Call) = mapping {
|
|
|
|
match mapped.file_id.expansion_info(db) {
|
|
|
|
Some(info) => mapping = info.map_token_up(db, mapped.as_ref())?,
|
|
|
|
None => return Some(mapped),
|
|
|
|
}
|
2020-12-08 18:01:27 +00:00
|
|
|
}
|
2022-02-11 21:12:16 +00:00
|
|
|
None
|
2020-12-08 18:01:27 +00:00
|
|
|
}
|
|
|
|
|
2020-01-05 20:32:18 +00:00
|
|
|
impl<N: AstNode> InFile<N> {
|
|
|
|
pub fn descendants<T: AstNode>(self) -> impl Iterator<Item = InFile<T>> {
|
|
|
|
self.value.syntax().descendants().filter_map(T::cast).map(move |n| self.with_value(n))
|
|
|
|
}
|
|
|
|
|
2021-11-03 20:12:36 +00:00
|
|
|
pub fn original_ast_node(self, db: &dyn db::AstDatabase) -> Option<InFile<N>> {
|
2022-01-31 12:56:14 +00:00
|
|
|
// This kind of upmapping can only be achieved in attribute expanded files,
|
2022-11-07 10:53:33 +00:00
|
|
|
// as we don't have node inputs otherwise and therefore can't find an `N` node in the input
|
2022-01-31 12:56:14 +00:00
|
|
|
if !self.file_id.is_macro() {
|
|
|
|
return Some(self);
|
|
|
|
} else if !self.file_id.is_attr_macro(db) {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(InFile { file_id, value: (first, last) }) =
|
|
|
|
ascend_node_border_tokens(db, self.syntax())
|
|
|
|
{
|
|
|
|
if file_id.is_macro() {
|
|
|
|
let range = first.text_range().cover(last.text_range());
|
|
|
|
tracing::error!("Failed mapping out of macro file for {:?}", range);
|
|
|
|
return None;
|
2021-11-03 20:12:36 +00:00
|
|
|
}
|
2022-01-31 12:56:14 +00:00
|
|
|
// FIXME: This heuristic is brittle and with the right macro may select completely unrelated nodes
|
|
|
|
let anc = algo::least_common_ancestor(&first.parent()?, &last.parent()?)?;
|
|
|
|
let value = anc.ancestors().find_map(N::cast)?;
|
|
|
|
return Some(InFile::new(file_id, value));
|
2021-11-03 20:12:36 +00:00
|
|
|
}
|
2022-01-31 12:56:14 +00:00
|
|
|
None
|
2021-11-03 20:12:36 +00:00
|
|
|
}
|
|
|
|
|
2020-01-05 20:32:18 +00:00
|
|
|
pub fn syntax(&self) -> InFile<&SyntaxNode> {
|
|
|
|
self.with_value(self.value.syntax())
|
|
|
|
}
|
|
|
|
}
|
2021-05-08 23:36:06 +00:00
|
|
|
|
2021-09-05 19:30:06 +00:00
|
|
|
/// In Rust, macros expand token trees to token trees. When we want to turn a
|
|
|
|
/// token tree into an AST node, we need to figure out what kind of AST node we
|
|
|
|
/// want: something like `foo` can be a type, an expression, or a pattern.
|
|
|
|
///
|
|
|
|
/// Naively, one would think that "what this expands to" is a property of a
|
|
|
|
/// particular macro: macro `m1` returns an item, while macro `m2` returns an
|
|
|
|
/// expression, etc. That's not the case -- macros are polymorphic in the
|
|
|
|
/// result, and can expand to any type of the AST node.
|
|
|
|
///
|
|
|
|
/// What defines the actual AST node is the syntactic context of the macro
|
|
|
|
/// invocation. As a contrived example, in `let T![*] = T![*];` the first `T`
|
|
|
|
/// expands to a pattern, while the second one expands to an expression.
|
|
|
|
///
|
|
|
|
/// `ExpandTo` captures this bit of information about a particular macro call
|
|
|
|
/// site.
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
|
|
pub enum ExpandTo {
|
|
|
|
Statements,
|
|
|
|
Items,
|
|
|
|
Pattern,
|
|
|
|
Type,
|
|
|
|
Expr,
|
|
|
|
}
|
2021-08-31 11:54:22 +00:00
|
|
|
|
2021-09-05 19:30:06 +00:00
|
|
|
impl ExpandTo {
|
|
|
|
pub fn from_call_site(call: &ast::MacroCall) -> ExpandTo {
|
|
|
|
use syntax::SyntaxKind::*;
|
|
|
|
|
|
|
|
let syn = call.syntax();
|
|
|
|
|
|
|
|
let parent = match syn.parent() {
|
|
|
|
Some(it) => it,
|
|
|
|
None => return ExpandTo::Statements,
|
|
|
|
};
|
|
|
|
|
2022-04-05 15:42:07 +00:00
|
|
|
// FIXME: macros in statement position are treated as expression statements, they should
|
|
|
|
// probably be their own statement kind. The *grand*parent indicates what's valid.
|
|
|
|
if parent.kind() == MACRO_EXPR
|
|
|
|
&& parent
|
|
|
|
.parent()
|
2022-08-31 14:58:11 +00:00
|
|
|
.map_or(false, |p| matches!(p.kind(), EXPR_STMT | STMT_LIST | MACRO_STMTS))
|
2022-04-05 15:42:07 +00:00
|
|
|
{
|
|
|
|
return ExpandTo::Statements;
|
|
|
|
}
|
|
|
|
|
2021-09-05 19:30:06 +00:00
|
|
|
match parent.kind() {
|
|
|
|
MACRO_ITEMS | SOURCE_FILE | ITEM_LIST => ExpandTo::Items,
|
2021-09-26 09:12:57 +00:00
|
|
|
MACRO_STMTS | EXPR_STMT | STMT_LIST => ExpandTo::Statements,
|
2021-09-05 19:30:06 +00:00
|
|
|
MACRO_PAT => ExpandTo::Pattern,
|
|
|
|
MACRO_TYPE => ExpandTo::Type,
|
|
|
|
|
2022-03-05 21:04:06 +00:00
|
|
|
ARG_LIST | ARRAY_EXPR | AWAIT_EXPR | BIN_EXPR | BREAK_EXPR | CALL_EXPR | CAST_EXPR
|
|
|
|
| CLOSURE_EXPR | FIELD_EXPR | FOR_EXPR | IF_EXPR | INDEX_EXPR | LET_EXPR
|
|
|
|
| MATCH_ARM | MATCH_EXPR | MATCH_GUARD | METHOD_CALL_EXPR | PAREN_EXPR | PATH_EXPR
|
|
|
|
| PREFIX_EXPR | RANGE_EXPR | RECORD_EXPR_FIELD | REF_EXPR | RETURN_EXPR | TRY_EXPR
|
2022-04-05 15:42:07 +00:00
|
|
|
| TUPLE_EXPR | WHILE_EXPR | MACRO_EXPR => ExpandTo::Expr,
|
2021-09-05 19:30:06 +00:00
|
|
|
_ => {
|
2022-04-05 15:42:07 +00:00
|
|
|
// Unknown , Just guess it is `Items`
|
|
|
|
ExpandTo::Items
|
2021-09-05 19:30:06 +00:00
|
|
|
}
|
2021-05-08 23:36:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-01-26 17:31:07 +00:00
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct UnresolvedMacro {
|
|
|
|
pub path: ModPath,
|
|
|
|
}
|