mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-26 11:59:49 +00:00
hygiene 2.0
This commit is contained in:
parent
e36b3f7b8c
commit
05f375eae2
43 changed files with 758 additions and 458 deletions
|
@ -7,11 +7,11 @@ use std::{
|
|||
|
||||
use crate::{
|
||||
db::ExpandDatabase,
|
||||
hygiene::Hygiene,
|
||||
name::{known, Name},
|
||||
hygiene::{SyntaxContextExt, Transparency},
|
||||
name::{known, AsName, Name},
|
||||
SpanMap,
|
||||
};
|
||||
use base_db::CrateId;
|
||||
use either::Either;
|
||||
use base_db::{span::SyntaxContextId, CrateId};
|
||||
use smallvec::SmallVec;
|
||||
use syntax::{ast, AstNode};
|
||||
|
||||
|
@ -38,6 +38,7 @@ pub enum PathKind {
|
|||
Crate,
|
||||
/// Absolute path (::foo)
|
||||
Abs,
|
||||
// FIXME: Remove this
|
||||
/// `$crate` from macro expansion
|
||||
DollarCrate(CrateId),
|
||||
}
|
||||
|
@ -46,7 +47,7 @@ impl ModPath {
|
|||
pub fn from_src(
|
||||
db: &dyn ExpandDatabase,
|
||||
path: ast::Path,
|
||||
hygiene: &Hygiene,
|
||||
hygiene: &SpanMap,
|
||||
) -> Option<ModPath> {
|
||||
convert_path(db, None, path, hygiene)
|
||||
}
|
||||
|
@ -193,7 +194,7 @@ fn convert_path(
|
|||
db: &dyn ExpandDatabase,
|
||||
prefix: Option<ModPath>,
|
||||
path: ast::Path,
|
||||
hygiene: &Hygiene,
|
||||
hygiene: &SpanMap,
|
||||
) -> Option<ModPath> {
|
||||
let prefix = match path.qualifier() {
|
||||
Some(qual) => Some(convert_path(db, prefix, qual, hygiene)?),
|
||||
|
@ -203,23 +204,26 @@ fn convert_path(
|
|||
let segment = path.segment()?;
|
||||
let mut mod_path = match segment.kind()? {
|
||||
ast::PathSegmentKind::Name(name_ref) => {
|
||||
match hygiene.name_ref_to_name(db, name_ref) {
|
||||
Either::Left(name) => {
|
||||
// no type args in use
|
||||
let mut res = prefix.unwrap_or_else(|| {
|
||||
ModPath::from_kind(
|
||||
segment.coloncolon_token().map_or(PathKind::Plain, |_| PathKind::Abs),
|
||||
)
|
||||
});
|
||||
res.segments.push(name);
|
||||
res
|
||||
}
|
||||
Either::Right(crate_id) => {
|
||||
return Some(ModPath::from_segments(
|
||||
PathKind::DollarCrate(crate_id),
|
||||
iter::empty(),
|
||||
))
|
||||
if name_ref.text() == "$crate" {
|
||||
if prefix.is_some() {
|
||||
return None;
|
||||
}
|
||||
resolve_crate_root(
|
||||
db,
|
||||
hygiene
|
||||
.span_for_range(name_ref.syntax().text_range())
|
||||
.map_or(SyntaxContextId::ROOT, |s| s.ctx),
|
||||
)
|
||||
.map(PathKind::DollarCrate)
|
||||
.map(ModPath::from_kind)?
|
||||
} else {
|
||||
let mut res = prefix.unwrap_or_else(|| {
|
||||
ModPath::from_kind(
|
||||
segment.coloncolon_token().map_or(PathKind::Plain, |_| PathKind::Abs),
|
||||
)
|
||||
});
|
||||
res.segments.push(name_ref.as_name());
|
||||
res
|
||||
}
|
||||
}
|
||||
ast::PathSegmentKind::SelfTypeKw => {
|
||||
|
@ -261,8 +265,15 @@ fn convert_path(
|
|||
// We follow what it did anyway :)
|
||||
if mod_path.segments.len() == 1 && mod_path.kind == PathKind::Plain {
|
||||
if let Some(_macro_call) = path.syntax().parent().and_then(ast::MacroCall::cast) {
|
||||
if let Some(crate_id) = hygiene.local_inner_macros(db, path) {
|
||||
mod_path.kind = PathKind::DollarCrate(crate_id);
|
||||
let syn_ctx = hygiene
|
||||
.span_for_range(segment.syntax().text_range())
|
||||
.map_or(SyntaxContextId::ROOT, |s| s.ctx);
|
||||
if let Some(macro_call_id) = db.lookup_intern_syntax_context(syn_ctx).outer_expn {
|
||||
if db.lookup_intern_macro_call(macro_call_id).def.local_inner {
|
||||
if let Some(crate_root) = resolve_crate_root(db, syn_ctx) {
|
||||
mod_path.kind = PathKind::DollarCrate(crate_root);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -270,6 +281,40 @@ fn convert_path(
|
|||
Some(mod_path)
|
||||
}
|
||||
|
||||
pub fn resolve_crate_root(db: &dyn ExpandDatabase, mut ctxt: SyntaxContextId) -> Option<CrateId> {
|
||||
// When resolving `$crate` from a `macro_rules!` invoked in a `macro`,
|
||||
// we don't want to pretend that the `macro_rules!` definition is in the `macro`
|
||||
// as described in `SyntaxContext::apply_mark`, so we ignore prepended opaque marks.
|
||||
// FIXME: This is only a guess and it doesn't work correctly for `macro_rules!`
|
||||
// definitions actually produced by `macro` and `macro` definitions produced by
|
||||
// `macro_rules!`, but at least such configurations are not stable yet.
|
||||
ctxt = ctxt.normalize_to_macro_rules(db);
|
||||
let mut iter = ctxt.marks(db).into_iter().rev().peekable();
|
||||
let mut result_mark = None;
|
||||
// Find the last opaque mark from the end if it exists.
|
||||
while let Some(&(mark, transparency)) = iter.peek() {
|
||||
if transparency == Transparency::Opaque {
|
||||
result_mark = Some(mark);
|
||||
iter.next();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Then find the last semi-transparent mark from the end if it exists.
|
||||
for (mark, transparency) in iter {
|
||||
if transparency == Transparency::SemiTransparent {
|
||||
result_mark = Some(mark);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
match result_mark {
|
||||
Some(Some(call)) => Some(db.lookup_intern_macro_call(call.into()).def.krate),
|
||||
Some(None) | None => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub use crate::name as __name;
|
||||
|
||||
#[macro_export]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue