Skip to content

Commit 4a961d1

Browse files
authored
Rollup merge of rust-lang#50192 - bobtwinkles:libsyntax_extensions, r=jseyfried
Add some utilities to `libsyntax` Adds a few functions to `Mark` and `Span` that I found useful in an upcoming refactor of NLL region error reporting. Also includes some new documentation based on my discussion with @jseyfried on IRC. r? @jseyfried
2 parents 8b36d9a + 73e0c1e commit 4a961d1

File tree

3 files changed

+50
-1
lines changed

3 files changed

+50
-1
lines changed

src/libproc_macro/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ impl Span {
270270
/// `self` was generated from, if any.
271271
#[unstable(feature = "proc_macro", issue = "38356")]
272272
pub fn parent(&self) -> Option<Span> {
273-
self.0.ctxt().outer().expn_info().map(|i| Span(i.call_site))
273+
self.0.parent().map(Span)
274274
}
275275

276276
/// The span for the origin source code that `self` was generated from. If

src/libsyntax_pos/hygiene.rs

+43
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use symbol::{Ident, Symbol};
2121

2222
use serialize::{Encodable, Decodable, Encoder, Decoder};
2323
use std::collections::HashMap;
24+
use rustc_data_structures::fx::FxHashSet;
2425
use std::fmt;
2526

2627
/// A SyntaxContext represents a chain of macro expansions (represented by marks).
@@ -117,6 +118,32 @@ impl Mark {
117118
true
118119
})
119120
}
121+
122+
/// Computes a mark such that both input marks are descendants of (or equal to) the returned
123+
/// mark. That is, the following holds:
124+
///
125+
/// ```rust
126+
/// let la = least_ancestor(a, b);
127+
/// assert!(a.is_descendant_of(la))
128+
/// assert!(b.is_descendant_of(la))
129+
/// ```
130+
pub fn least_ancestor(mut a: Mark, mut b: Mark) -> Mark {
131+
HygieneData::with(|data| {
132+
// Compute the path from a to the root
133+
let mut a_path = FxHashSet::<Mark>();
134+
while a != Mark::root() {
135+
a_path.insert(a);
136+
a = data.marks[a.0 as usize].parent;
137+
}
138+
139+
// While the path from b to the root hasn't intersected, move up the tree
140+
while !a_path.contains(&b) {
141+
b = data.marks[b.0 as usize].parent;
142+
}
143+
144+
b
145+
})
146+
}
120147
}
121148

122149
pub struct HygieneData {
@@ -238,6 +265,22 @@ impl SyntaxContext {
238265
})
239266
}
240267

268+
/// Pulls a single mark off of the syntax context. This effectively moves the
269+
/// context up one macro definition level. That is, if we have a nested macro
270+
/// definition as follows:
271+
///
272+
/// ```rust
273+
/// macro_rules! f {
274+
/// macro_rules! g {
275+
/// ...
276+
/// }
277+
/// }
278+
/// ```
279+
///
280+
/// and we have a SyntaxContext that is referring to something declared by an invocation
281+
/// of g (call it g1), calling remove_mark will result in the SyntaxContext for the
282+
/// invocation of f that created g1.
283+
/// Returns the mark that was removed.
241284
pub fn remove_mark(&mut self) -> Mark {
242285
HygieneData::with(|data| {
243286
let outer_mark = data.syntax_contexts[self.0 as usize].outer_mark;

src/libsyntax_pos/lib.rs

+6
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,12 @@ impl Span {
291291
self.ctxt().outer().expn_info().map(|info| info.call_site.source_callsite()).unwrap_or(self)
292292
}
293293

294+
/// The `Span` for the tokens in the previous macro expansion from which `self` was generated,
295+
/// if any
296+
pub fn parent(self) -> Option<Span> {
297+
self.ctxt().outer().expn_info().map(|i| i.call_site)
298+
}
299+
294300
/// Return the source callee.
295301
///
296302
/// Returns None if the supplied span has no expansion trace,

0 commit comments

Comments
 (0)