2019-10-30 15:41:50 +00:00
|
|
|
//! This modules handles hygiene information.
|
|
|
|
//!
|
|
|
|
//! Specifically, `ast` + `Hygiene` allows you to create a `Name`. Note that, at
|
|
|
|
//! this moment, this is horribly incomplete and handles only `$crate`.
|
2023-12-18 12:39:44 +00:00
|
|
|
|
|
|
|
// FIXME: Consider moving this into the span crate.
|
|
|
|
|
2023-11-24 15:38:48 +00:00
|
|
|
use std::iter;
|
|
|
|
|
2023-12-18 12:30:41 +00:00
|
|
|
use span::{MacroCallId, Span, SyntaxContextId};
|
2019-10-30 15:41:50 +00:00
|
|
|
|
2023-11-17 18:07:31 +00:00
|
|
|
use crate::db::ExpandDatabase;
|
2019-10-30 15:41:50 +00:00
|
|
|
|
2023-12-02 12:03:46 +00:00
|
|
|
#[derive(Copy, Clone, Hash, PartialEq, Eq)]
|
2023-10-06 12:47:11 +00:00
|
|
|
pub struct SyntaxContextData {
|
2023-11-17 18:07:31 +00:00
|
|
|
pub outer_expn: Option<MacroCallId>,
|
|
|
|
pub outer_transparency: Transparency,
|
|
|
|
pub parent: SyntaxContextId,
|
2023-10-06 12:47:11 +00:00
|
|
|
/// This context, but with all transparent and semi-transparent expansions filtered away.
|
2023-11-17 18:07:31 +00:00
|
|
|
pub opaque: SyntaxContextId,
|
2023-10-06 12:47:11 +00:00
|
|
|
/// This context, but with all transparent expansions filtered away.
|
2023-11-17 18:07:31 +00:00
|
|
|
pub opaque_and_semitransparent: SyntaxContextId,
|
|
|
|
}
|
|
|
|
|
2023-12-02 12:03:46 +00:00
|
|
|
impl std::fmt::Debug for SyntaxContextData {
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
f.debug_struct("SyntaxContextData")
|
|
|
|
.field("outer_expn", &self.outer_expn)
|
|
|
|
.field("outer_transparency", &self.outer_transparency)
|
|
|
|
.field("parent", &self.parent)
|
|
|
|
.field("opaque", &self.opaque)
|
|
|
|
.field("opaque_and_semitransparent", &self.opaque_and_semitransparent)
|
|
|
|
.finish()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-11-17 18:07:31 +00:00
|
|
|
impl SyntaxContextData {
|
|
|
|
pub fn root() -> Self {
|
|
|
|
SyntaxContextData {
|
|
|
|
outer_expn: None,
|
|
|
|
outer_transparency: Transparency::Opaque,
|
|
|
|
parent: SyntaxContextId::ROOT,
|
|
|
|
opaque: SyntaxContextId::ROOT,
|
|
|
|
opaque_and_semitransparent: SyntaxContextId::ROOT,
|
|
|
|
}
|
|
|
|
}
|
2023-12-02 12:03:46 +00:00
|
|
|
|
|
|
|
pub fn fancy_debug(
|
|
|
|
self,
|
|
|
|
self_id: SyntaxContextId,
|
|
|
|
db: &dyn ExpandDatabase,
|
|
|
|
f: &mut std::fmt::Formatter<'_>,
|
|
|
|
) -> std::fmt::Result {
|
|
|
|
write!(f, "#{self_id} parent: #{}, outer_mark: (", self.parent)?;
|
|
|
|
match self.outer_expn {
|
|
|
|
Some(id) => {
|
|
|
|
write!(f, "{:?}::{{{{expn{:?}}}}}", db.lookup_intern_macro_call(id).krate, id)?
|
|
|
|
}
|
|
|
|
None => write!(f, "root")?,
|
|
|
|
}
|
|
|
|
write!(f, ", {:?})", self.outer_transparency)
|
|
|
|
}
|
2023-10-06 12:47:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// A property of a macro expansion that determines how identifiers
|
|
|
|
/// produced by that expansion are resolved.
|
|
|
|
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Hash, Debug)]
|
|
|
|
pub enum Transparency {
|
|
|
|
/// Identifier produced by a transparent expansion is always resolved at call-site.
|
|
|
|
/// Call-site spans in procedural macros, hygiene opt-out in `macro` should use this.
|
|
|
|
Transparent,
|
|
|
|
/// Identifier produced by a semi-transparent expansion may be resolved
|
|
|
|
/// either at call-site or at definition-site.
|
|
|
|
/// If it's a local variable, label or `$crate` then it's resolved at def-site.
|
|
|
|
/// Otherwise it's resolved at call-site.
|
|
|
|
/// `macro_rules` macros behave like this, built-in macros currently behave like this too,
|
|
|
|
/// but that's an implementation detail.
|
|
|
|
SemiTransparent,
|
|
|
|
/// Identifier produced by an opaque expansion is always resolved at definition-site.
|
|
|
|
/// Def-site spans in procedural macros, identifiers from `macro` by default use this.
|
|
|
|
Opaque,
|
|
|
|
}
|
|
|
|
|
2023-12-18 12:30:41 +00:00
|
|
|
pub fn span_with_def_site_ctxt(db: &dyn ExpandDatabase, span: Span, expn_id: MacroCallId) -> Span {
|
2023-11-24 15:38:48 +00:00
|
|
|
span_with_ctxt_from_mark(db, span, expn_id, Transparency::Opaque)
|
|
|
|
}
|
|
|
|
|
2023-12-18 12:30:41 +00:00
|
|
|
pub fn span_with_call_site_ctxt(db: &dyn ExpandDatabase, span: Span, expn_id: MacroCallId) -> Span {
|
2023-11-24 15:38:48 +00:00
|
|
|
span_with_ctxt_from_mark(db, span, expn_id, Transparency::Transparent)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn span_with_mixed_site_ctxt(
|
|
|
|
db: &dyn ExpandDatabase,
|
2023-12-18 12:30:41 +00:00
|
|
|
span: Span,
|
2023-11-24 15:38:48 +00:00
|
|
|
expn_id: MacroCallId,
|
2023-12-18 12:30:41 +00:00
|
|
|
) -> Span {
|
2023-11-24 15:38:48 +00:00
|
|
|
span_with_ctxt_from_mark(db, span, expn_id, Transparency::SemiTransparent)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn span_with_ctxt_from_mark(
|
|
|
|
db: &dyn ExpandDatabase,
|
2023-12-18 12:30:41 +00:00
|
|
|
span: Span,
|
2023-11-24 15:38:48 +00:00
|
|
|
expn_id: MacroCallId,
|
|
|
|
transparency: Transparency,
|
2023-12-18 12:30:41 +00:00
|
|
|
) -> Span {
|
|
|
|
Span { ctx: apply_mark(db, SyntaxContextId::ROOT, expn_id, transparency), ..span }
|
2023-11-24 15:38:48 +00:00
|
|
|
}
|
|
|
|
|
2023-10-06 12:47:11 +00:00
|
|
|
pub(super) fn apply_mark(
|
2023-11-17 18:07:31 +00:00
|
|
|
db: &dyn ExpandDatabase,
|
|
|
|
ctxt: SyntaxContextId,
|
|
|
|
call_id: MacroCallId,
|
|
|
|
transparency: Transparency,
|
2023-10-06 12:47:11 +00:00
|
|
|
) -> SyntaxContextId {
|
2023-11-17 18:07:31 +00:00
|
|
|
if transparency == Transparency::Opaque {
|
|
|
|
return apply_mark_internal(db, ctxt, Some(call_id), transparency);
|
2019-10-30 15:41:50 +00:00
|
|
|
}
|
|
|
|
|
2023-12-20 11:53:46 +00:00
|
|
|
let call_site_ctxt = db.lookup_intern_macro_call(call_id).call_site.ctx;
|
2023-11-17 18:07:31 +00:00
|
|
|
let mut call_site_ctxt = if transparency == Transparency::SemiTransparent {
|
|
|
|
call_site_ctxt.normalize_to_macros_2_0(db)
|
|
|
|
} else {
|
|
|
|
call_site_ctxt.normalize_to_macro_rules(db)
|
|
|
|
};
|
2020-05-01 03:23:03 +00:00
|
|
|
|
2023-11-24 15:38:48 +00:00
|
|
|
if call_site_ctxt.is_root() {
|
2023-11-17 18:07:31 +00:00
|
|
|
return apply_mark_internal(db, ctxt, Some(call_id), transparency);
|
2021-01-03 10:47:57 +00:00
|
|
|
}
|
2021-01-02 12:25:05 +00:00
|
|
|
|
2023-11-17 18:07:31 +00:00
|
|
|
// Otherwise, `expn_id` is a macros 1.0 definition and the call site is in a
|
|
|
|
// macros 2.0 expansion, i.e., a macros 1.0 invocation is in a macros 2.0 definition.
|
|
|
|
//
|
|
|
|
// In this case, the tokens from the macros 1.0 definition inherit the hygiene
|
|
|
|
// at their invocation. That is, we pretend that the macros 1.0 definition
|
|
|
|
// was defined at its invocation (i.e., inside the macros 2.0 definition)
|
|
|
|
// so that the macros 2.0 definition remains hygienic.
|
|
|
|
//
|
|
|
|
// See the example at `test/ui/hygiene/legacy_interaction.rs`.
|
|
|
|
for (call_id, transparency) in ctxt.marks(db) {
|
|
|
|
call_site_ctxt = apply_mark_internal(db, call_site_ctxt, call_id, transparency);
|
2021-01-04 02:53:31 +00:00
|
|
|
}
|
2023-11-17 18:07:31 +00:00
|
|
|
apply_mark_internal(db, call_site_ctxt, Some(call_id), transparency)
|
2021-01-04 02:53:31 +00:00
|
|
|
}
|
|
|
|
|
2023-11-17 18:07:31 +00:00
|
|
|
fn apply_mark_internal(
|
|
|
|
db: &dyn ExpandDatabase,
|
|
|
|
ctxt: SyntaxContextId,
|
|
|
|
call_id: Option<MacroCallId>,
|
|
|
|
transparency: Transparency,
|
|
|
|
) -> SyntaxContextId {
|
|
|
|
let syntax_context_data = db.lookup_intern_syntax_context(ctxt);
|
2024-01-01 11:54:30 +00:00
|
|
|
let mut opaque = handle_self_ref(ctxt, syntax_context_data.opaque);
|
|
|
|
let mut opaque_and_semitransparent =
|
|
|
|
handle_self_ref(ctxt, syntax_context_data.opaque_and_semitransparent);
|
2023-11-17 18:07:31 +00:00
|
|
|
|
|
|
|
if transparency >= Transparency::Opaque {
|
2024-01-01 11:54:30 +00:00
|
|
|
let parent = opaque;
|
|
|
|
// Unlike rustc, with salsa we can't prefetch the to be allocated ID to create cycles with
|
|
|
|
// salsa when interning, so we use a sentinel value that effectively means the current
|
|
|
|
// syntax context.
|
2023-11-17 18:07:31 +00:00
|
|
|
let new_opaque = SyntaxContextId::SELF_REF;
|
|
|
|
opaque = db.intern_syntax_context(SyntaxContextData {
|
|
|
|
outer_expn: call_id,
|
|
|
|
outer_transparency: transparency,
|
|
|
|
parent,
|
|
|
|
opaque: new_opaque,
|
|
|
|
opaque_and_semitransparent: new_opaque,
|
|
|
|
});
|
|
|
|
}
|
2021-01-04 02:53:31 +00:00
|
|
|
|
2023-11-17 18:07:31 +00:00
|
|
|
if transparency >= Transparency::SemiTransparent {
|
2024-01-01 11:54:30 +00:00
|
|
|
let parent = opaque_and_semitransparent;
|
|
|
|
// Unlike rustc, with salsa we can't prefetch the to be allocated ID to create cycles with
|
|
|
|
// salsa when interning, so we use a sentinel value that effectively means the current
|
|
|
|
// syntax context.
|
2023-11-17 18:07:31 +00:00
|
|
|
let new_opaque_and_semitransparent = SyntaxContextId::SELF_REF;
|
|
|
|
opaque_and_semitransparent = db.intern_syntax_context(SyntaxContextData {
|
|
|
|
outer_expn: call_id,
|
|
|
|
outer_transparency: transparency,
|
|
|
|
parent,
|
|
|
|
opaque,
|
|
|
|
opaque_and_semitransparent: new_opaque_and_semitransparent,
|
|
|
|
});
|
|
|
|
}
|
2021-01-04 02:53:31 +00:00
|
|
|
|
2023-11-17 18:07:31 +00:00
|
|
|
let parent = ctxt;
|
|
|
|
db.intern_syntax_context(SyntaxContextData {
|
|
|
|
outer_expn: call_id,
|
|
|
|
outer_transparency: transparency,
|
|
|
|
parent,
|
|
|
|
opaque,
|
|
|
|
opaque_and_semitransparent,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
pub trait SyntaxContextExt {
|
|
|
|
fn normalize_to_macro_rules(self, db: &dyn ExpandDatabase) -> Self;
|
|
|
|
fn normalize_to_macros_2_0(self, db: &dyn ExpandDatabase) -> Self;
|
|
|
|
fn parent_ctxt(self, db: &dyn ExpandDatabase) -> Self;
|
2023-12-06 10:53:28 +00:00
|
|
|
fn remove_mark(&mut self, db: &dyn ExpandDatabase) -> (Option<MacroCallId>, Transparency);
|
2023-11-17 18:07:31 +00:00
|
|
|
fn outer_mark(self, db: &dyn ExpandDatabase) -> (Option<MacroCallId>, Transparency);
|
|
|
|
fn marks(self, db: &dyn ExpandDatabase) -> Vec<(Option<MacroCallId>, Transparency)>;
|
|
|
|
}
|
2021-01-04 02:53:31 +00:00
|
|
|
|
2023-11-17 18:07:31 +00:00
|
|
|
#[inline(always)]
|
|
|
|
fn handle_self_ref(p: SyntaxContextId, n: SyntaxContextId) -> SyntaxContextId {
|
|
|
|
match n {
|
|
|
|
SyntaxContextId::SELF_REF => p,
|
|
|
|
_ => n,
|
2021-01-04 02:53:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-11-17 18:07:31 +00:00
|
|
|
impl SyntaxContextExt for SyntaxContextId {
|
|
|
|
fn normalize_to_macro_rules(self, db: &dyn ExpandDatabase) -> Self {
|
|
|
|
handle_self_ref(self, db.lookup_intern_syntax_context(self).opaque_and_semitransparent)
|
|
|
|
}
|
|
|
|
fn normalize_to_macros_2_0(self, db: &dyn ExpandDatabase) -> Self {
|
|
|
|
handle_self_ref(self, db.lookup_intern_syntax_context(self).opaque)
|
|
|
|
}
|
|
|
|
fn parent_ctxt(self, db: &dyn ExpandDatabase) -> Self {
|
|
|
|
db.lookup_intern_syntax_context(self).parent
|
|
|
|
}
|
|
|
|
fn outer_mark(self, db: &dyn ExpandDatabase) -> (Option<MacroCallId>, Transparency) {
|
|
|
|
let data = db.lookup_intern_syntax_context(self);
|
|
|
|
(data.outer_expn, data.outer_transparency)
|
|
|
|
}
|
2023-12-06 10:53:28 +00:00
|
|
|
fn remove_mark(&mut self, db: &dyn ExpandDatabase) -> (Option<MacroCallId>, Transparency) {
|
|
|
|
let data = db.lookup_intern_syntax_context(*self);
|
|
|
|
*self = data.parent;
|
|
|
|
(data.outer_expn, data.outer_transparency)
|
|
|
|
}
|
2023-11-24 15:38:48 +00:00
|
|
|
fn marks(self, db: &dyn ExpandDatabase) -> Vec<(Option<MacroCallId>, Transparency)> {
|
|
|
|
let mut marks = marks_rev(self, db).collect::<Vec<_>>();
|
2023-11-17 18:07:31 +00:00
|
|
|
marks.reverse();
|
|
|
|
marks
|
2020-05-01 03:23:03 +00:00
|
|
|
}
|
2019-10-30 15:41:50 +00:00
|
|
|
}
|
2023-11-17 18:07:31 +00:00
|
|
|
|
2023-11-24 15:38:48 +00:00
|
|
|
// FIXME: Make this a SyntaxContextExt method once we have RPIT
|
|
|
|
pub fn marks_rev(
|
|
|
|
ctxt: SyntaxContextId,
|
|
|
|
db: &dyn ExpandDatabase,
|
|
|
|
) -> impl Iterator<Item = (Option<MacroCallId>, Transparency)> + '_ {
|
|
|
|
iter::successors(Some(ctxt), move |&mark| {
|
|
|
|
Some(mark.parent_ctxt(db)).filter(|&it| it != SyntaxContextId::ROOT)
|
|
|
|
})
|
|
|
|
.map(|ctx| ctx.outer_mark(db))
|
|
|
|
}
|
2024-01-25 09:07:29 +00:00
|
|
|
|
|
|
|
pub(crate) fn dump_syntax_contexts(db: &dyn ExpandDatabase) -> String {
|
|
|
|
use crate::db::{InternMacroCallLookupQuery, InternSyntaxContextLookupQuery};
|
|
|
|
use base_db::salsa::debug::DebugQueryTable;
|
|
|
|
|
|
|
|
let mut s = String::from("Expansions:");
|
|
|
|
let mut entries = InternMacroCallLookupQuery.in_db(db).entries::<Vec<_>>();
|
|
|
|
entries.sort_by_key(|e| e.key);
|
|
|
|
for e in entries {
|
|
|
|
let id = e.key;
|
|
|
|
let expn_data = e.value.as_ref().unwrap();
|
|
|
|
s.push_str(&format!(
|
|
|
|
"\n{:?}: parent: {:?}, call_site_ctxt: {:?}, def_site_ctxt: {:?}, kind: {:?}",
|
|
|
|
id,
|
|
|
|
expn_data.kind.file_id(),
|
|
|
|
expn_data.call_site,
|
|
|
|
SyntaxContextId::ROOT, // FIXME expn_data.def_site,
|
|
|
|
expn_data.kind.descr(),
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
s.push_str("\n\nSyntaxContexts:\n");
|
|
|
|
let mut entries = InternSyntaxContextLookupQuery.in_db(db).entries::<Vec<_>>();
|
|
|
|
entries.sort_by_key(|e| e.key);
|
|
|
|
for e in entries {
|
|
|
|
struct SyntaxContextDebug<'a>(
|
|
|
|
&'a dyn ExpandDatabase,
|
|
|
|
SyntaxContextId,
|
|
|
|
&'a SyntaxContextData,
|
|
|
|
);
|
|
|
|
|
|
|
|
impl<'a> std::fmt::Debug for SyntaxContextDebug<'a> {
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
self.2.fancy_debug(self.1, self.0, f)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
stdx::format_to!(s, "{:?}\n", SyntaxContextDebug(db, e.key, &e.value.unwrap()));
|
|
|
|
}
|
|
|
|
s
|
|
|
|
}
|