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
|
|
|
|
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-06-09 16:02:31 +00:00
|
|
|
pub mod builtin_attr;
|
2019-12-05 14:10:33 +00:00
|
|
|
pub mod builtin_derive;
|
2019-11-10 03:03:24 +00:00
|
|
|
pub mod builtin_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;
|
2019-10-29 11:55:39 +00:00
|
|
|
|
2021-05-29 18:32:57 +00:00
|
|
|
use base_db::ProcMacroKind;
|
2021-03-19 18:56:13 +00:00
|
|
|
use either::Either;
|
2021-05-08 23:36:06 +00:00
|
|
|
|
2020-11-26 15:48:17 +00:00
|
|
|
pub use mbe::{ExpandError, ExpandResult};
|
2021-05-08 23:36:06 +00:00
|
|
|
pub use parser::FragmentKind;
|
2020-11-26 15:48:17 +00:00
|
|
|
|
2021-08-20 11:49:28 +00:00
|
|
|
use std::{hash::Hash, iter, sync::Arc};
|
2019-10-29 12:11:42 +00:00
|
|
|
|
2020-12-08 18:01:27 +00:00
|
|
|
use base_db::{impl_intern_key, salsa, CrateId, FileId, FileRange};
|
2020-08-12 16:26:51 +00:00
|
|
|
use syntax::{
|
2021-01-15 17:15:33 +00:00
|
|
|
algo::skip_trivia_token,
|
2021-08-21 16:06:03 +00:00
|
|
|
ast::{self, AstNode, AttrsOwner},
|
2020-12-08 18:01:27 +00:00
|
|
|
Direction, SyntaxNode, SyntaxToken, TextRange, TextSize,
|
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,
|
|
|
|
builtin_attr::BuiltinAttrExpander,
|
|
|
|
builtin_derive::BuiltinDeriveExpander,
|
|
|
|
builtin_macro::{BuiltinFnLikeExpander, EagerExpander},
|
2021-08-21 16:06:03 +00:00
|
|
|
db::TokenExpander,
|
2021-08-19 20:53:00 +00:00
|
|
|
proc_macro::ProcMacroExpander,
|
|
|
|
};
|
2019-10-29 12:11:42 +00:00
|
|
|
|
2019-11-22 17:11:33 +00:00
|
|
|
#[cfg(test)]
|
|
|
|
mod test_db;
|
|
|
|
|
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),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<FileId> for HirFileId {
|
|
|
|
fn from(id: FileId) -> Self {
|
|
|
|
HirFileId(HirFileIdRepr::FileId(id))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<MacroFile> for HirFileId {
|
|
|
|
fn from(id: MacroFile) -> Self {
|
|
|
|
HirFileId(HirFileIdRepr::MacroFile(id))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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 {
|
2019-10-29 12:11:42 +00:00
|
|
|
match self.0 {
|
|
|
|
HirFileIdRepr::FileId(file_id) => file_id,
|
|
|
|
HirFileIdRepr::MacroFile(macro_file) => {
|
2021-05-19 18:19:08 +00:00
|
|
|
let loc: MacroCallLoc = db.lookup_intern_macro(macro_file.macro_call_id);
|
|
|
|
let file_id = match &loc.eager {
|
|
|
|
Some(EagerCallInfo { included_file: Some(file), .. }) => (*file).into(),
|
|
|
|
_ => loc.kind.file_id(),
|
2020-02-17 11:32:13 +00:00
|
|
|
};
|
2020-03-02 06:05:15 +00:00
|
|
|
file_id.original_file(db)
|
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-05-19 18:19:08 +00:00
|
|
|
let loc: MacroCallLoc = db.lookup_intern_macro(macro_file.macro_call_id);
|
|
|
|
|
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) => {
|
|
|
|
let loc: MacroCallLoc = db.lookup_intern_macro(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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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-05-19 18:19:08 +00:00
|
|
|
let loc: MacroCallLoc = db.lookup_intern_macro(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
|
|
|
|
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()?,
|
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)
|
|
|
|
.attrs()
|
2021-08-22 17:12:45 +00:00
|
|
|
.nth(invoc_attr_index as usize)?
|
|
|
|
.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
|
|
|
|
|
|
|
let macro_def = db.macro_def(loc.def)?;
|
2020-11-24 20:57:51 +00:00
|
|
|
let (parse, exp_map) = db.parse_macro_expansion(macro_file).value?;
|
2019-11-09 04:00:46 +00:00
|
|
|
let macro_arg = db.macro_arg(macro_file.macro_call_id)?;
|
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
|
2020-07-29 22:23:03 +00:00
|
|
|
pub fn is_builtin_derive(&self, db: &dyn db::AstDatabase) -> Option<InFile<ast::Item>> {
|
2020-01-12 10:08:53 +00:00
|
|
|
match self.0 {
|
|
|
|
HirFileIdRepr::FileId(_) => None,
|
|
|
|
HirFileIdRepr::MacroFile(macro_file) => {
|
2021-05-19 18:19:08 +00:00
|
|
|
let loc: MacroCallLoc = db.lookup_intern_macro(macro_file.macro_call_id);
|
2020-01-12 10:08:53 +00:00
|
|
|
let item = 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,
|
|
|
|
};
|
2020-07-29 22:23:03 +00:00
|
|
|
Some(item.with_value(ast::Item::cast(item.value.clone())?))
|
2020-01-12 10:08:53 +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) => {
|
|
|
|
let loc: MacroCallLoc = db.lookup_intern_macro(macro_file.macro_call_id);
|
|
|
|
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
|
|
|
|
|
|
|
pub fn is_macro(self) -> bool {
|
|
|
|
matches!(self.0, HirFileIdRepr::MacroFile(_))
|
|
|
|
}
|
2019-10-29 12:11:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
|
|
pub struct MacroFile {
|
|
|
|
macro_call_id: MacroCallId,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// `MacroCallId` identifies a particular macro invocation, like
|
|
|
|
/// `println!("Hello, {}", world)`.
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
2021-05-19 18:19:08 +00:00
|
|
|
pub struct MacroCallId(salsa::InternId);
|
|
|
|
impl_intern_key!(MacroCallId);
|
2020-02-17 11:32:13 +00:00
|
|
|
|
2019-10-29 12:11:42 +00:00
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
2019-11-11 10:45:55 +00:00
|
|
|
pub struct MacroDefId {
|
2020-12-15 19:33:05 +00:00
|
|
|
pub krate: CrateId,
|
2019-11-11 10:45:55 +00:00
|
|
|
pub kind: MacroDefKind,
|
2020-05-01 03:23:03 +00:00
|
|
|
|
|
|
|
pub local_inner: bool,
|
2019-11-10 03:03:24 +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 {
|
|
|
|
db.intern_macro(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>> {
|
2021-03-18 14:37:14 +00:00
|
|
|
let id = match &self.kind {
|
2021-05-29 18:32:57 +00:00
|
|
|
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
|
|
|
};
|
2021-03-19 18:56:13 +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(..))
|
|
|
|
}
|
2019-11-26 17:33:08 +00:00
|
|
|
}
|
|
|
|
|
2019-11-10 03:03:24 +00:00
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
2019-11-11 10:45:55 +00:00
|
|
|
pub enum MacroDefKind {
|
2021-03-18 14:37:14 +00:00
|
|
|
Declarative(AstId<ast::Macro>),
|
|
|
|
BuiltIn(BuiltinFnLikeExpander, AstId<ast::Macro>),
|
2019-12-05 14:10:33 +00:00
|
|
|
// FIXME: maybe just Builtin and rename BuiltinFnLikeExpander to BuiltinExpander
|
2021-06-09 16:02:31 +00:00
|
|
|
BuiltInAttr(BuiltinAttrExpander, AstId<ast::Macro>),
|
2021-03-18 14:37:14 +00:00
|
|
|
BuiltInDerive(BuiltinDeriveExpander, AstId<ast::Macro>),
|
|
|
|
BuiltInEager(EagerExpander, AstId<ast::Macro>),
|
2021-05-29 18:32:57 +00:00
|
|
|
ProcMacro(ProcMacroExpander, ProcMacroKind, AstId<ast::Fn>),
|
2019-10-29 12:11:42 +00:00
|
|
|
}
|
|
|
|
|
2021-05-19 18:19:08 +00:00
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
|
|
struct EagerCallInfo {
|
|
|
|
/// NOTE: This can be *either* the expansion result, *or* the argument to the eager macro!
|
2021-05-19 18:23:26 +00:00
|
|
|
arg_or_expansion: Arc<tt::Subtree>,
|
2021-05-19 18:19:08 +00:00
|
|
|
included_file: Option<FileId>,
|
|
|
|
}
|
|
|
|
|
2019-10-29 12:11:42 +00:00
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
|
|
pub struct MacroCallLoc {
|
2021-05-19 21:35:09 +00:00
|
|
|
pub def: MacroDefId,
|
2020-06-11 10:08:24 +00:00
|
|
|
pub(crate) krate: CrateId,
|
2021-05-19 18:19:08 +00:00
|
|
|
eager: Option<EagerCallInfo>,
|
2020-11-26 19:09:54 +00:00
|
|
|
pub kind: MacroCallKind,
|
2019-12-05 14:10:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
|
|
pub enum MacroCallKind {
|
2021-05-10 20:54:17 +00:00
|
|
|
FnLike {
|
|
|
|
ast_id: AstId<ast::MacroCall>,
|
|
|
|
fragment: FragmentKind,
|
|
|
|
},
|
|
|
|
Derive {
|
|
|
|
ast_id: AstId<ast::Item>,
|
|
|
|
derive_name: String,
|
|
|
|
/// 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,
|
|
|
|
},
|
2021-05-31 11:37:11 +00:00
|
|
|
Attr {
|
|
|
|
ast_id: AstId<ast::Item>,
|
|
|
|
attr_name: String,
|
2021-08-21 16:19:18 +00:00
|
|
|
attr_args: (tt::Subtree, mbe::TokenMap),
|
2021-05-31 11:37:11 +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,
|
|
|
|
},
|
2019-12-05 14:10:33 +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 {
|
2019-12-05 14:10:33 +00:00
|
|
|
match self {
|
2021-04-08 18:43:07 +00:00
|
|
|
MacroCallKind::FnLike { ast_id, .. } => ast_id.file_id,
|
2021-05-31 11:37:11 +00:00
|
|
|
MacroCallKind::Derive { ast_id, .. } | MacroCallKind::Attr { ast_id, .. } => {
|
|
|
|
ast_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())
|
|
|
|
}
|
2021-05-31 11:37:11 +00:00
|
|
|
MacroCallKind::Derive { ast_id, .. } | 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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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())
|
|
|
|
}
|
2021-05-31 11:37:11 +00:00
|
|
|
MacroCallKind::Derive { ast_id, .. } | 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
|
|
|
|
|
|
|
fn fragment_kind(&self) -> FragmentKind {
|
|
|
|
match self {
|
|
|
|
MacroCallKind::FnLike { fragment, .. } => *fragment,
|
|
|
|
MacroCallKind::Derive { .. } => FragmentKind::Items,
|
2021-05-31 11:37:11 +00:00
|
|
|
MacroCallKind::Attr { .. } => FragmentKind::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>,
|
2019-12-05 14:10:33 +00:00
|
|
|
arg: InFile<SyntaxNode>,
|
2021-08-21 16:06:03 +00:00
|
|
|
/// The `macro_rules!` arguments or attribute input.
|
|
|
|
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>,
|
2019-11-17 17:15:55 +00:00
|
|
|
macro_arg: Arc<(tt::Subtree, mbe::TokenMap)>,
|
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
|
|
|
}
|
|
|
|
|
2019-12-14 17:46:39 +00:00
|
|
|
pub use mbe::Origin;
|
2019-12-14 17:20:07 +00:00
|
|
|
|
2019-11-03 14:46:12 +00:00
|
|
|
impl ExpansionInfo {
|
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-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-08-21 16:06:03 +00:00
|
|
|
let token_id = if let Some(item) = item {
|
|
|
|
let call_id = match self.expanded.file_id.0 {
|
|
|
|
HirFileIdRepr::FileId(_) => return None,
|
|
|
|
HirFileIdRepr::MacroFile(macro_file) => macro_file.macro_call_id,
|
|
|
|
};
|
|
|
|
let loc = db.lookup_intern_macro(call_id);
|
|
|
|
|
|
|
|
let token_range = token.value.text_range();
|
|
|
|
match &loc.kind {
|
|
|
|
MacroCallKind::Attr { attr_args, invoc_attr_index, .. } => {
|
|
|
|
let attr = item.attrs().nth(*invoc_attr_index as usize)?;
|
|
|
|
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();
|
|
|
|
let range = token.value.text_range().checked_sub(attr_input_start)?;
|
|
|
|
let token_id =
|
2021-08-21 16:19:18 +00:00
|
|
|
self.macro_arg_shift.shift(attr_args.1.token_by_range(range)?);
|
2021-08-21 16:06:03 +00:00
|
|
|
Some(token_id)
|
|
|
|
}
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
|
|
|
|
|
|
|
let token_id = match token_id {
|
|
|
|
Some(token_id) => token_id,
|
|
|
|
None => {
|
|
|
|
let range =
|
|
|
|
token.value.text_range().checked_sub(self.arg.value.text_range().start())?;
|
|
|
|
let token_id = self.macro_arg.1.token_by_range(range)?;
|
|
|
|
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-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)> {
|
|
|
|
let token_id = self.exp_map.token_by_range(token.value.text_range())?;
|
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-08-21 16:06:03 +00:00
|
|
|
let call_id = match self.expanded.file_id.0 {
|
|
|
|
HirFileIdRepr::FileId(_) => return None,
|
|
|
|
HirFileIdRepr::MacroFile(macro_file) => macro_file.macro_call_id,
|
|
|
|
};
|
|
|
|
let loc = db.lookup_intern_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;
|
2021-08-21 16:19:18 +00:00
|
|
|
(&attr_args.1, self.attr_input_or_mac_def.clone()?.syntax().cloned())
|
2021-08-21 16:06:03 +00:00
|
|
|
}
|
|
|
|
None => (&self.macro_arg.1, self.arg.clone()),
|
|
|
|
},
|
|
|
|
_ => 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-08-21 16:06:03 +00:00
|
|
|
(
|
|
|
|
TokenExpander::MacroRules { def_site_token_map, .. }
|
|
|
|
| TokenExpander::MacroDef { def_site_token_map, .. },
|
|
|
|
Some(tt),
|
|
|
|
) => (def_site_token_map, tt.syntax().cloned()),
|
|
|
|
_ => 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.
|
|
|
|
// FIXME: isn't this just a `Source<FileAstId<N>>` ?
|
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-15 21:40:54 +00:00
|
|
|
// Similarly, naming here is stupid...
|
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
|
|
|
}
|
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
|
|
|
}
|
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))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-06 20:46:18 +00:00
|
|
|
impl InFile<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,
|
2020-02-12 17:19:55 +00:00
|
|
|
) -> impl Iterator<Item = InFile<SyntaxNode>> + '_ {
|
2021-08-20 11:49:28 +00:00
|
|
|
iter::successors(Some(self), move |node| match node.value.parent() {
|
2019-12-06 20:46:18 +00:00
|
|
|
Some(parent) => Some(node.with_value(parent)),
|
|
|
|
None => {
|
|
|
|
let parent_node = node.file_id.call_node(db)?;
|
|
|
|
Some(parent_node)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
2020-01-05 20:32:18 +00:00
|
|
|
|
2020-12-08 18:01:27 +00:00
|
|
|
impl<'a> InFile<&'a SyntaxNode> {
|
2021-08-19 20:53:00 +00:00
|
|
|
/// Falls back to the macro call range if the node cannot be mapped up fully.
|
2020-12-08 18:01:27 +00:00
|
|
|
pub fn original_file_range(self, db: &dyn db::AstDatabase) -> FileRange {
|
2021-08-19 20:53:00 +00:00
|
|
|
if let Some(res) = self.original_file_range_opt(db) {
|
|
|
|
return res;
|
2020-12-08 18:01:27 +00:00
|
|
|
}
|
|
|
|
|
2020-12-08 18:11:12 +00:00
|
|
|
// Fall back to whole macro call.
|
|
|
|
let mut node = self.cloned();
|
|
|
|
while let Some(call_node) = node.file_id.call_node(db) {
|
|
|
|
node = call_node;
|
2020-12-08 18:01:27 +00:00
|
|
|
}
|
|
|
|
|
2020-12-08 18:11:12 +00:00
|
|
|
let orig_file = node.file_id.original_file(db);
|
|
|
|
assert_eq!(node.file_id, orig_file.into());
|
2021-08-19 20:53:00 +00:00
|
|
|
|
2020-12-08 18:11:12 +00:00
|
|
|
FileRange { file_id: orig_file, range: node.value.text_range() }
|
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> {
|
|
|
|
match original_range_opt(db, self) {
|
|
|
|
Some(range) => {
|
|
|
|
let original_file = range.file_id.original_file(db);
|
|
|
|
if range.file_id != original_file.into() {
|
2021-08-15 12:46:13 +00:00
|
|
|
tracing::error!("Failed mapping up more for {:?}", range);
|
2021-08-19 20:53:00 +00:00
|
|
|
}
|
|
|
|
Some(FileRange { file_id: original_file, range: range.value })
|
|
|
|
}
|
|
|
|
_ if !self.file_id.is_macro() => Some(FileRange {
|
|
|
|
file_id: self.file_id.original_file(db),
|
|
|
|
range: self.value.text_range(),
|
|
|
|
}),
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
2020-12-08 18:01:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn original_range_opt(
|
|
|
|
db: &dyn db::AstDatabase,
|
|
|
|
node: InFile<&SyntaxNode>,
|
|
|
|
) -> Option<InFile<TextRange>> {
|
|
|
|
let expansion = node.file_id.expansion_info(db)?;
|
|
|
|
|
|
|
|
// the input node has only one token ?
|
|
|
|
let single = skip_trivia_token(node.value.first_token()?, Direction::Next)?
|
|
|
|
== skip_trivia_token(node.value.last_token()?, Direction::Prev)?;
|
|
|
|
|
2021-03-17 01:19:40 +00:00
|
|
|
node.value.descendants().find_map(|it| {
|
2020-12-08 18:01:27 +00:00
|
|
|
let first = skip_trivia_token(it.first_token()?, Direction::Next)?;
|
|
|
|
let first = ascend_call_token(db, &expansion, node.with_value(first))?;
|
|
|
|
|
|
|
|
let last = skip_trivia_token(it.last_token()?, Direction::Prev)?;
|
|
|
|
let last = ascend_call_token(db, &expansion, node.with_value(last))?;
|
|
|
|
|
|
|
|
if (!single && first == last) || (first.file_id != last.file_id) {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
|
|
|
Some(first.with_value(first.value.text_range().cover(last.value.text_range())))
|
2021-03-17 01:19:40 +00:00
|
|
|
})
|
2020-12-08 18:01:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn ascend_call_token(
|
|
|
|
db: &dyn db::AstDatabase,
|
|
|
|
expansion: &ExpansionInfo,
|
|
|
|
token: InFile<SyntaxToken>,
|
|
|
|
) -> Option<InFile<SyntaxToken>> {
|
2021-08-21 16:06:03 +00:00
|
|
|
let (mapped, origin) = expansion.map_token_up(db, token.as_ref())?;
|
2020-12-08 18:01:27 +00:00
|
|
|
if origin != Origin::Call {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
if let Some(info) = mapped.file_id.expansion_info(db) {
|
|
|
|
return ascend_call_token(db, &info, mapped);
|
|
|
|
}
|
|
|
|
Some(mapped)
|
|
|
|
}
|
|
|
|
|
2020-02-12 17:19:55 +00:00
|
|
|
impl InFile<SyntaxToken> {
|
|
|
|
pub fn ancestors_with_macros(
|
|
|
|
self,
|
2020-03-13 15:05:46 +00:00
|
|
|
db: &dyn db::AstDatabase,
|
2020-02-12 17:19:55 +00:00
|
|
|
) -> impl Iterator<Item = InFile<SyntaxNode>> + '_ {
|
2021-08-20 13:20:18 +00:00
|
|
|
self.value.parent().into_iter().flat_map({
|
|
|
|
let file_id = self.file_id;
|
|
|
|
move |parent| InFile::new(file_id, parent).ancestors_with_macros(db)
|
|
|
|
})
|
2020-02-12 17:19:55 +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))
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn syntax(&self) -> InFile<&SyntaxNode> {
|
|
|
|
self.with_value(self.value.syntax())
|
|
|
|
}
|
2021-08-18 14:30:09 +00:00
|
|
|
|
|
|
|
pub fn nodes_with_attributes<'db>(
|
|
|
|
self,
|
|
|
|
db: &'db dyn db::AstDatabase,
|
|
|
|
) -> impl Iterator<Item = InFile<N>> + 'db
|
|
|
|
where
|
|
|
|
N: 'db,
|
|
|
|
{
|
2021-08-20 11:49:28 +00:00
|
|
|
iter::successors(Some(self), move |node| {
|
2021-08-18 14:30:09 +00:00
|
|
|
let InFile { file_id, value } = node.file_id.call_node(db)?;
|
|
|
|
N::cast(value).map(|n| InFile::new(file_id, n))
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn node_with_attributes(self, db: &dyn db::AstDatabase) -> InFile<N> {
|
2021-08-20 11:49:28 +00:00
|
|
|
self.nodes_with_attributes(db).last().unwrap()
|
2021-08-18 14:30:09 +00:00
|
|
|
}
|
2020-01-05 20:32:18 +00:00
|
|
|
}
|
2021-05-08 23:36:06 +00:00
|
|
|
|
|
|
|
/// Given a `MacroCallId`, return what `FragmentKind` it belongs to.
|
|
|
|
/// FIXME: Not completed
|
|
|
|
pub fn to_fragment_kind(call: &ast::MacroCall) -> FragmentKind {
|
|
|
|
use syntax::SyntaxKind::*;
|
|
|
|
|
|
|
|
let syn = call.syntax();
|
|
|
|
|
|
|
|
let parent = match syn.parent() {
|
|
|
|
Some(it) => it,
|
|
|
|
None => return FragmentKind::Statements,
|
|
|
|
};
|
|
|
|
|
|
|
|
match parent.kind() {
|
2021-08-31 11:54:22 +00:00
|
|
|
MACRO_ITEMS | SOURCE_FILE | ITEM_LIST => FragmentKind::Items,
|
|
|
|
MACRO_STMTS | EXPR_STMT | BLOCK_EXPR => FragmentKind::Statements,
|
2021-05-08 23:36:06 +00:00
|
|
|
MACRO_PAT => FragmentKind::Pattern,
|
|
|
|
MACRO_TYPE => FragmentKind::Type,
|
2021-08-31 11:54:22 +00:00
|
|
|
|
|
|
|
ARG_LIST | TRY_EXPR | TUPLE_EXPR | PAREN_EXPR | ARRAY_EXPR | FOR_EXPR | PATH_EXPR
|
|
|
|
| CLOSURE_EXPR | CONDITION | BREAK_EXPR | RETURN_EXPR | MATCH_EXPR | MATCH_ARM
|
|
|
|
| MATCH_GUARD | RECORD_EXPR_FIELD | CALL_EXPR | INDEX_EXPR | METHOD_CALL_EXPR
|
|
|
|
| FIELD_EXPR | AWAIT_EXPR | CAST_EXPR | REF_EXPR | PREFIX_EXPR | RANGE_EXPR | BIN_EXPR => {
|
|
|
|
FragmentKind::Expr
|
|
|
|
}
|
2021-05-08 23:36:06 +00:00
|
|
|
LET_STMT => {
|
|
|
|
// FIXME: Handle LHS Pattern
|
|
|
|
FragmentKind::Expr
|
|
|
|
}
|
2021-08-31 11:54:22 +00:00
|
|
|
|
2021-05-08 23:36:06 +00:00
|
|
|
_ => {
|
|
|
|
// Unknown , Just guess it is `Items`
|
|
|
|
FragmentKind::Items
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|