diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 0de900ca75ebf..c5433983fcc7d 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -1291,7 +1291,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::TraitRef::new(self.tcx, into_def_id, [expr_ty, expected_ty]), )) { - let span = find_local_most_ancestor_suggestable_span(expr.span); + let span = expr.span.find_local_most_ancestor_suggestable_span(); let mut sugg = if expr.precedence().order() >= PREC_POSTFIX { vec![(span.shrink_to_hi(), ".into()".to_owned())] @@ -1896,7 +1896,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { None => sugg.to_string(), }; - let span = find_local_most_ancestor_suggestable_span(expr.span); + let span = expr.span.find_local_most_ancestor_suggestable_span(); err.span_suggestion_verbose(span.shrink_to_hi(), msg, sugg, Applicability::HasPlaceholders); return true; } @@ -3171,19 +3171,3 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } - -/// For suggestion span, recursively try to look for the ancestor span which shares the same syntax -/// context as the initially provided `span`. This will recursively look into local macros until the -/// span inside the most ancestor local macro is found. It will stop recursing as soon as the -/// syntax context of a potential parent callsite changes, such as if the potential parent callsite -/// is in a foreign macro. This helps to prevent leaking implementation details from upstream -/// crates and stdlib crates that the user likely have no control over. -fn find_local_most_ancestor_suggestable_span(initial_span: Span) -> Span { - let mut span = initial_span; - while initial_span.eq_ctxt(span) - && let Some(parent_callsite) = span.parent_callsite() - { - span = parent_callsite; - } - span -} diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 7ce879807cae1..3fcd75c85f239 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -743,6 +743,45 @@ impl Span { Some(self) } + /// Recursively walk down the expansion ancestors to find the most ancestor span with the same + /// [`SyntaxContext`] as `initial`. + /// + /// This method is suitable for peeling through *local* macro expansions to find the "innermost" + /// span that is still local and shares the same [`SyntaxContext`]. For example, given + /// + /// ``` + /// macro_rules! outer { + /// ($x: expr) => { + /// inner!($x) + /// } + /// } + /// + /// macro_rules! inner { + /// ($x: expr) => { + /// format!("error: {}", $x) + /// //~^ ERROR mismatched types + /// } + /// } + /// + /// fn bar(x: &str) -> Result<(), Box> { + /// Err(outer!(x)) + /// } + /// ``` + /// + /// if provided the `initial` span of `outer!(x)` inside `bar`, this method will recurse + /// the parent callsites until we reach `format!("error: {}", $x)`, at which point it is the + /// most ancestor span that is both still local and shares the same [`SyntaxContext`] as the + /// `initial` span. + pub fn find_local_most_ancestor_suggestable_span(self) -> Span { + let mut cur = self; + while cur.eq_ctxt(self) + && let Some(parent_callsite) = cur.parent_callsite() + { + cur = parent_callsite; + } + cur + } + /// Edition of the crate from which this span came. pub fn edition(self) -> edition::Edition { self.ctxt().edition()