rustc_span: Optimize syntax context comparisons

Including comparisons with root context
This commit is contained in:
Vadim Petrochenkov 2024-01-02 23:32:40 +03:00
parent b8c207435c
commit 90d11d6448
13 changed files with 76 additions and 52 deletions

View File

@ -72,7 +72,7 @@ pub(super) fn parse(
// `SyntaxContext::root()` from a foreign crate will // `SyntaxContext::root()` from a foreign crate will
// have the edition of that crate (which we manually // have the edition of that crate (which we manually
// retrieve via the `edition` parameter). // retrieve via the `edition` parameter).
if span.ctxt().is_root() { if !span.from_expansion() {
edition edition
} else { } else {
span.edition() span.edition()

View File

@ -549,7 +549,9 @@ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
impl<'tcx> LateLintPass<'tcx> for SpanUseEqCtxt { impl<'tcx> LateLintPass<'tcx> for SpanUseEqCtxt {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) {
if let ExprKind::Binary(BinOp { node: BinOpKind::Eq, .. }, lhs, rhs) = expr.kind { if let ExprKind::Binary(BinOp { node: BinOpKind::Eq | BinOpKind::Ne, .. }, lhs, rhs) =
expr.kind
{
if is_span_ctxt_call(cx, lhs) && is_span_ctxt_call(cx, rhs) { if is_span_ctxt_call(cx, lhs) && is_span_ctxt_call(cx, rhs) {
cx.emit_spanned_lint(SPAN_USE_EQ_CTXT, expr.span, SpanUseEqCtxtDiag); cx.emit_spanned_lint(SPAN_USE_EQ_CTXT, expr.span, SpanUseEqCtxtDiag);
} }

View File

@ -111,10 +111,11 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc
let mut arg_span = arg.span; let mut arg_span = arg.span;
let mut arg_macro = None; let mut arg_macro = None;
while !span.contains(arg_span) { while !span.contains(arg_span) {
let expn = arg_span.ctxt().outer_expn_data(); let ctxt = arg_span.ctxt();
if expn.is_root() { if ctxt.is_root() {
break; break;
} }
let expn = ctxt.outer_expn_data();
arg_macro = expn.macro_def_id; arg_macro = expn.macro_def_id;
arg_span = expn.call_site; arg_span = expn.call_site;
} }

View File

@ -1630,7 +1630,7 @@ fn report_unused(
let from_macro = non_shorthands let from_macro = non_shorthands
.iter() .iter()
.find(|(_, pat_span, ident_span)| { .find(|(_, pat_span, ident_span)| {
pat_span.ctxt() != ident_span.ctxt() && pat_span.from_expansion() !pat_span.eq_ctxt(*ident_span) && pat_span.from_expansion()
}) })
.map(|(_, pat_span, _)| *pat_span); .map(|(_, pat_span, _)| *pat_span);
let non_shorthands = non_shorthands let non_shorthands = non_shorthands

View File

@ -295,11 +295,13 @@ pub fn outer_expn_is_descendant_of(self, ctxt: SyntaxContext) -> bool {
pub fn expansion_cause(mut self) -> Option<Span> { pub fn expansion_cause(mut self) -> Option<Span> {
let mut last_macro = None; let mut last_macro = None;
loop { loop {
// Fast path to avoid locking.
if self == ExpnId::root() {
break;
}
let expn_data = self.expn_data(); let expn_data = self.expn_data();
// Stop going up the backtrace once include! is encountered // Stop going up the backtrace once include! is encountered
if expn_data.is_root() if expn_data.kind == ExpnKind::Macro(MacroKind::Bang, sym::include) {
|| expn_data.kind == ExpnKind::Macro(MacroKind::Bang, sym::include)
{
break; break;
} }
self = expn_data.call_site.ctxt().outer_expn(); self = expn_data.call_site.ctxt().outer_expn();
@ -433,7 +435,7 @@ fn remove_mark(&self, ctxt: &mut SyntaxContext) -> (ExpnId, Transparency) {
fn marks(&self, mut ctxt: SyntaxContext) -> Vec<(ExpnId, Transparency)> { fn marks(&self, mut ctxt: SyntaxContext) -> Vec<(ExpnId, Transparency)> {
let mut marks = Vec::new(); let mut marks = Vec::new();
while ctxt != SyntaxContext::root() { while !ctxt.is_root() {
debug!("marks: getting parent of {:?}", ctxt); debug!("marks: getting parent of {:?}", ctxt);
marks.push(self.outer_mark(ctxt)); marks.push(self.outer_mark(ctxt));
ctxt = self.parent_ctxt(ctxt); ctxt = self.parent_ctxt(ctxt);

View File

@ -541,10 +541,6 @@ pub fn with_hi(self, hi: BytePos) -> Span {
self.data().with_hi(hi) self.data().with_hi(hi)
} }
#[inline] #[inline]
pub fn eq_ctxt(self, other: Span) -> bool {
self.data_untracked().ctxt == other.data_untracked().ctxt
}
#[inline]
pub fn with_ctxt(self, ctxt: SyntaxContext) -> Span { pub fn with_ctxt(self, ctxt: SyntaxContext) -> Span {
self.data_untracked().with_ctxt(ctxt) self.data_untracked().with_ctxt(ctxt)
} }
@ -565,7 +561,7 @@ pub fn is_visible(self, sm: &SourceMap) -> bool {
/// Returns `true` if this span comes from any kind of macro, desugaring or inlining. /// Returns `true` if this span comes from any kind of macro, desugaring or inlining.
#[inline] #[inline]
pub fn from_expansion(self) -> bool { pub fn from_expansion(self) -> bool {
self.ctxt() != SyntaxContext::root() !self.ctxt().is_root()
} }
/// Returns `true` if `span` originates in a macro's expansion where debuginfo should be /// Returns `true` if `span` originates in a macro's expansion where debuginfo should be
@ -654,15 +650,15 @@ pub fn trim_start(self, other: Span) -> Option<Span> {
/// Returns the source span -- this is either the supplied span, or the span for /// Returns the source span -- this is either the supplied span, or the span for
/// the macro callsite that expanded to it. /// the macro callsite that expanded to it.
pub fn source_callsite(self) -> Span { pub fn source_callsite(self) -> Span {
let expn_data = self.ctxt().outer_expn_data(); let ctxt = self.ctxt();
if !expn_data.is_root() { expn_data.call_site.source_callsite() } else { self } if !ctxt.is_root() { ctxt.outer_expn_data().call_site.source_callsite() } else { self }
} }
/// The `Span` for the tokens in the previous macro expansion from which `self` was generated, /// The `Span` for the tokens in the previous macro expansion from which `self` was generated,
/// if any. /// if any.
pub fn parent_callsite(self) -> Option<Span> { pub fn parent_callsite(self) -> Option<Span> {
let expn_data = self.ctxt().outer_expn_data(); let ctxt = self.ctxt();
if !expn_data.is_root() { Some(expn_data.call_site) } else { None } (!ctxt.is_root()).then(|| ctxt.outer_expn_data().call_site)
} }
/// Walk down the expansion ancestors to find a span that's contained within `outer`. /// Walk down the expansion ancestors to find a span that's contained within `outer`.
@ -747,15 +743,14 @@ pub fn at_least_rust_2024(self) -> bool {
/// else returns the `ExpnData` for the macro definition /// else returns the `ExpnData` for the macro definition
/// corresponding to the source callsite. /// corresponding to the source callsite.
pub fn source_callee(self) -> Option<ExpnData> { pub fn source_callee(self) -> Option<ExpnData> {
let expn_data = self.ctxt().outer_expn_data(); let mut ctxt = self.ctxt();
let mut opt_expn_data = None;
// Create an iterator of call site expansions while !ctxt.is_root() {
iter::successors(Some(expn_data), |expn_data| { let expn_data = ctxt.outer_expn_data();
Some(expn_data.call_site.ctxt().outer_expn_data()) ctxt = expn_data.call_site.ctxt();
}) opt_expn_data = Some(expn_data);
// Find the last expansion which is not root }
.take_while(|expn_data| !expn_data.is_root()) opt_expn_data
.last()
} }
/// Checks if a span is "internal" to a macro in which `#[unstable]` /// Checks if a span is "internal" to a macro in which `#[unstable]`
@ -796,11 +791,12 @@ pub fn macro_backtrace(mut self) -> impl Iterator<Item = ExpnData> {
let mut prev_span = DUMMY_SP; let mut prev_span = DUMMY_SP;
iter::from_fn(move || { iter::from_fn(move || {
loop { loop {
let expn_data = self.ctxt().outer_expn_data(); let ctxt = self.ctxt();
if expn_data.is_root() { if ctxt.is_root() {
return None; return None;
} }
let expn_data = ctxt.outer_expn_data();
let is_recursive = expn_data.call_site.source_equal(prev_span); let is_recursive = expn_data.call_site.source_equal(prev_span);
prev_span = self; prev_span = self;

View File

@ -23,9 +23,15 @@
/// otherwise return the call site span up to the `enclosing_sp` by /// otherwise return the call site span up to the `enclosing_sp` by
/// following the `expn_data` chain. /// following the `expn_data` chain.
pub fn original_sp(sp: Span, enclosing_sp: Span) -> Span { pub fn original_sp(sp: Span, enclosing_sp: Span) -> Span {
let expn_data1 = sp.ctxt().outer_expn_data(); let ctxt = sp.ctxt();
let expn_data2 = enclosing_sp.ctxt().outer_expn_data(); if ctxt.is_root() {
if expn_data1.is_root() || !expn_data2.is_root() && expn_data1.call_site == expn_data2.call_site return sp;
}
let enclosing_ctxt = enclosing_sp.ctxt();
let expn_data1 = ctxt.outer_expn_data();
if !enclosing_ctxt.is_root()
&& expn_data1.call_site == enclosing_ctxt.outer_expn_data().call_site
{ {
sp sp
} else { } else {

View File

@ -18,7 +18,7 @@ impl SourceMap {
/// * the LHS span must start at or before the RHS span. /// * the LHS span must start at or before the RHS span.
fn merge_spans(&self, sp_lhs: Span, sp_rhs: Span) -> Option<Span> { fn merge_spans(&self, sp_lhs: Span, sp_rhs: Span) -> Option<Span> {
// Ensure we're at the same expansion ID. // Ensure we're at the same expansion ID.
if sp_lhs.ctxt() != sp_rhs.ctxt() { if !sp_lhs.eq_ctxt(sp_rhs) {
return None; return None;
} }

View File

@ -210,12 +210,10 @@ pub fn is_dummy(self) -> bool {
} }
} }
/// This function is used as a fast path when decoding the full `SpanData` is not necessary. // Returns either syntactic context, if it can be retrieved without taking the interner lock,
/// It's a cut-down version of `data_untracked`. // or an index into the interner if it cannot.
#[cfg_attr(not(test), rustc_diagnostic_item = "SpanCtxt")] fn inline_ctxt(self) -> Result<SyntaxContext, usize> {
#[inline] Ok(if self.len_with_tag_or_marker != BASE_LEN_INTERNED_MARKER {
pub fn ctxt(self) -> SyntaxContext {
if self.len_with_tag_or_marker != BASE_LEN_INTERNED_MARKER {
if self.len_with_tag_or_marker & PARENT_TAG == 0 { if self.len_with_tag_or_marker & PARENT_TAG == 0 {
// Inline-context format. // Inline-context format.
SyntaxContext::from_u32(self.ctxt_or_parent_or_marker as u32) SyntaxContext::from_u32(self.ctxt_or_parent_or_marker as u32)
@ -223,17 +221,36 @@ pub fn ctxt(self) -> SyntaxContext {
// Inline-parent format. We know that the SyntaxContext is root. // Inline-parent format. We know that the SyntaxContext is root.
SyntaxContext::root() SyntaxContext::root()
} }
} else { } else if self.ctxt_or_parent_or_marker != CTXT_INTERNED_MARKER {
if self.ctxt_or_parent_or_marker != CTXT_INTERNED_MARKER {
// Partially-interned format. This path avoids looking up the // Partially-interned format. This path avoids looking up the
// interned value, and is the whole point of the // interned value, and is the whole point of the
// partially-interned format. // partially-interned format.
SyntaxContext::from_u32(self.ctxt_or_parent_or_marker as u32) SyntaxContext::from_u32(self.ctxt_or_parent_or_marker as u32)
} else { } else {
// Fully-interned format. // Fully-interned format.
let index = self.lo_or_index; return Err(self.lo_or_index as usize);
with_span_interner(|interner| interner.spans[index as usize].ctxt) })
} }
/// This function is used as a fast path when decoding the full `SpanData` is not necessary.
/// It's a cut-down version of `data_untracked`.
#[cfg_attr(not(test), rustc_diagnostic_item = "SpanCtxt")]
#[inline]
pub fn ctxt(self) -> SyntaxContext {
self.inline_ctxt()
.unwrap_or_else(|index| with_span_interner(|interner| interner.spans[index].ctxt))
}
#[inline]
pub fn eq_ctxt(self, other: Span) -> bool {
match (self.inline_ctxt(), other.inline_ctxt()) {
(Ok(ctxt1), Ok(ctxt2)) => ctxt1 == ctxt2,
(Ok(ctxt), Err(index)) | (Err(index), Ok(ctxt)) => {
with_span_interner(|interner| ctxt == interner.spans[index].ctxt)
}
(Err(index1), Err(index2)) => with_span_interner(|interner| {
interner.spans[index1].ctxt == interner.spans[index2].ctxt
}),
} }
} }
} }

View File

@ -145,7 +145,7 @@ pub(super) fn check<'tcx>(
if cast_from.kind() == cast_to.kind() && !in_external_macro(cx.sess(), expr.span) { if cast_from.kind() == cast_to.kind() && !in_external_macro(cx.sess(), expr.span) {
if let Some(id) = path_to_local(cast_expr) if let Some(id) = path_to_local(cast_expr)
&& let Some(span) = cx.tcx.hir().opt_span(id) && let Some(span) = cx.tcx.hir().opt_span(id)
&& span.ctxt() != cast_expr.span.ctxt() && !span.eq_ctxt(cast_expr.span)
{ {
// Binding context is different than the identifiers context. // Binding context is different than the identifiers context.
// Weird macro wizardry could be involved here. // Weird macro wizardry could be involved here.

View File

@ -118,7 +118,7 @@ fn suggestion(
vis.visit_ty(impl_.self_ty); vis.visit_ty(impl_.self_ty);
for target in &vis.found { for target in &vis.found {
if item.span.ctxt() != target.span().ctxt() { if !item.span.eq_ctxt(target.span()) {
return; return;
} }

View File

@ -225,7 +225,7 @@ fn check_fn(
_: LocalDefId, _: LocalDefId,
) { ) {
if (!matches!(kind, FnKind::Closure) && matches!(decl.output, FnRetTy::DefaultReturn(_))) if (!matches!(kind, FnKind::Closure) && matches!(decl.output, FnRetTy::DefaultReturn(_)))
|| span.ctxt() != body.value.span.ctxt() || !span.eq_ctxt(body.value.span)
|| in_external_macro(cx.sess(), span) || in_external_macro(cx.sess(), span)
{ {
return; return;

View File

@ -67,7 +67,7 @@ pub(super) fn check<'tcx>(
} }
} }
if unwrap_arg.span.ctxt() != map_span.ctxt() { if !unwrap_arg.span.eq_ctxt(map_span) {
return; return;
} }