minor: Add some basic docs for spans/hygiene handling

This commit is contained in:
Lukas Wirth 2024-03-01 14:31:25 +01:00
parent 9efa23c4da
commit eb7a4f293e
5 changed files with 184 additions and 129 deletions

View file

@ -5,7 +5,7 @@ use either::Either;
use limit::Limit;
use mbe::{syntax_node_to_token_tree, ValueResult};
use rustc_hash::FxHashSet;
use span::SyntaxContextId;
use span::{SyntaxContextData, SyntaxContextId};
use syntax::{
ast::{self, HasAttrs},
AstNode, Parse, SyntaxError, SyntaxNode, SyntaxToken, T,
@ -19,10 +19,7 @@ use crate::{
builtin_fn_macro::EagerExpander,
declarative::DeclarativeMacroExpander,
fixup::{self, reverse_fixups, SyntaxFixupUndoInfo},
hygiene::{
span_with_call_site_ctxt, span_with_def_site_ctxt, span_with_mixed_site_ctxt,
SyntaxContextData,
},
hygiene::{span_with_call_site_ctxt, span_with_def_site_ctxt, span_with_mixed_site_ctxt},
proc_macro::ProcMacros,
span_map::{RealSpanMap, SpanMap, SpanMapRef},
tt, AstId, BuiltinAttrExpander, BuiltinDeriveExpander, BuiltinFnLikeExpander,

View file

@ -1,94 +1,34 @@
//! This modules handles hygiene information.
//! Machinery for hygienic macros.
//!
//! Specifically, `ast` + `Hygiene` allows you to create a `Name`. Note that, at
//! this moment, this is horribly incomplete and handles only `$crate`.
// FIXME: Consider moving this into the span crate.
//! Inspired by Matthew Flatt et al., “Macros That Work Together: Compile-Time Bindings, Partial
//! Expansion, and Definition Contexts,” *Journal of Functional Programming* 22, no. 2
//! (March 1, 2012): 181216, <https://doi.org/10.1017/S0956796812000093>.
//!
//! Also see https://rustc-dev-guide.rust-lang.org/macro-expansion.html#hygiene-and-hierarchies
//!
//! # The Expansion Order Hierarchy
//!
//! `ExpnData` in rustc, rust-analyzer's version is [`MacroCallLoc`]. Traversing the hierarchy
//! upwards can be achieved by walking up [`MacroCallLoc::kind`]'s contained file id, as
//! [`MacroFile`]s are interned [`MacroCallLoc`]s.
//!
//! # The Macro Definition Hierarchy
//!
//! `SyntaxContextData` in rustc and rust-analyzer. Basically the same in both.
//!
//! # The Call-site Hierarchy
//!
//! `ExpnData::call_site` in rustc, [`MacroCallLoc::call_site`] in rust-analyzer.
// FIXME: Move this into the span crate? Not quite possible today as that depends on `MacroCallLoc`
// which contains a bunch of unrelated things
use std::iter;
use base_db::salsa::{self, InternValue};
use span::{MacroCallId, Span, SyntaxContextId};
use span::{MacroCallId, Span, SyntaxContextData, SyntaxContextId};
use crate::db::{ExpandDatabase, InternSyntaxContextQuery};
#[derive(Copy, Clone, Hash, PartialEq, Eq)]
pub struct SyntaxContextData {
pub outer_expn: Option<MacroCallId>,
pub outer_transparency: Transparency,
pub parent: SyntaxContextId,
/// This context, but with all transparent and semi-transparent expansions filtered away.
pub opaque: SyntaxContextId,
/// This context, but with all transparent expansions filtered away.
pub opaque_and_semitransparent: SyntaxContextId,
}
impl InternValue for SyntaxContextData {
type Key = (SyntaxContextId, Option<MacroCallId>, Transparency);
fn into_key(&self) -> Self::Key {
(self.parent, self.outer_expn, self.outer_transparency)
}
}
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()
}
}
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,
}
}
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)
}
}
/// 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,
}
pub use span::Transparency;
pub fn span_with_def_site_ctxt(db: &dyn ExpandDatabase, span: Span, expn_id: MacroCallId) -> Span {
span_with_ctxt_from_mark(db, span, expn_id, Transparency::Opaque)
@ -157,6 +97,8 @@ fn apply_mark_internal(
call_id: Option<MacroCallId>,
transparency: Transparency,
) -> SyntaxContextId {
use base_db::salsa;
let syntax_context_data = db.lookup_intern_syntax_context(ctxt);
let mut opaque = syntax_context_data.opaque;
let mut opaque_and_semitransparent = syntax_context_data.opaque_and_semitransparent;
@ -199,6 +141,7 @@ fn apply_mark_internal(
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;
@ -277,9 +220,26 @@ pub(crate) fn dump_syntax_contexts(db: &dyn ExpandDatabase) -> String {
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)
fancy_debug(self.2, self.1, self.0, f)
}
}
pub fn fancy_debug(
this: &SyntaxContextData,
self_id: SyntaxContextId,
db: &dyn ExpandDatabase,
f: &mut std::fmt::Formatter<'_>,
) -> std::fmt::Result {
write!(f, "#{self_id} parent: #{}, outer_mark: (", this.parent)?;
match this.outer_expn {
Some(id) => {
write!(f, "{:?}::{{{{expn{:?}}}}}", db.lookup_intern_macro_call(id).krate, id)?
}
None => write!(f, "root")?,
}
write!(f, ", {:?})", this.outer_transparency)
}
stdx::format_to!(s, "{:?}\n", SyntaxContextDebug(db, e.key, &e.value.unwrap()));
}
s

View file

@ -32,7 +32,7 @@ use std::{fmt, hash::Hash};
use base_db::{salsa::impl_intern_value_trivial, CrateId, Edition, FileId};
use either::Either;
use span::{FileRange, HirFileIdRepr, Span, SyntaxContextId};
use span::{FileRange, HirFileIdRepr, Span, SyntaxContextData, SyntaxContextId};
use syntax::{
ast::{self, AstNode},
SyntaxNode, SyntaxToken, TextRange, TextSize,
@ -44,7 +44,6 @@ use crate::{
builtin_derive_macro::BuiltinDeriveExpander,
builtin_fn_macro::{BuiltinFnLikeExpander, EagerExpander},
db::{ExpandDatabase, TokenExpander},
hygiene::SyntaxContextData,
mod_path::ModPath,
proc_macro::{CustomProcMacroExpander, ProcMacroKind},
span_map::{ExpansionSpanMap, SpanMap},