Skip to content

Add method find_ancestor_not_from_macro and find_ancestor_not_from_extern_macro to fix find_oldest_ancestor_in_same_ctxt #144268

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,7 @@ impl<'tcx> BorrowExplanation<'tcx> {
}
}
} else if let LocalInfo::BlockTailTemp(info) = local_decl.local_info() {
let sp = info.span.find_oldest_ancestor_in_same_ctxt();
let sp = info.span.find_ancestor_not_from_macro().unwrap_or(info.span);
if info.tail_result_is_ignored {
// #85581: If the first mutable borrow's scope contains
// the second borrow, this suggestion isn't helpful.
Expand Down
10 changes: 8 additions & 2 deletions compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1393,7 +1393,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.macro_backtrace()
.any(|x| matches!(x.kind, ExpnKind::Macro(MacroKind::Attr | MacroKind::Derive, ..)))
{
let span = expr.span.find_oldest_ancestor_in_same_ctxt();
let span = expr
.span
.find_ancestor_not_from_extern_macro(&self.tcx.sess.source_map())
.unwrap_or(expr.span);

let mut sugg = if self.precedence(expr) >= ExprPrecedence::Unambiguous {
vec![(span.shrink_to_hi(), ".into()".to_owned())]
Expand Down Expand Up @@ -2060,7 +2063,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
None => sugg.to_string(),
};

let span = expr.span.find_oldest_ancestor_in_same_ctxt();
let span = expr
.span
.find_ancestor_not_from_extern_macro(&self.tcx.sess.source_map())
.unwrap_or(expr.span);
err.span_suggestion_verbose(span.shrink_to_hi(), msg, sugg, Applicability::HasPlaceholders);
true
}
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_lint/src/unused.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
let mut op_warned = false;

if let Some(must_use_op) = must_use_op {
let span = expr.span.find_oldest_ancestor_in_same_ctxt();
let span = expr.span.find_ancestor_not_from_macro().unwrap_or(expr.span);
cx.emit_span_lint(
UNUSED_MUST_USE,
expr.span,
Expand Down Expand Up @@ -511,7 +511,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
);
}
MustUsePath::Def(span, def_id, reason) => {
let span = span.find_oldest_ancestor_in_same_ctxt();
let span = span.find_ancestor_not_from_macro().unwrap_or(*span);
cx.emit_span_lint(
UNUSED_MUST_USE,
span,
Expand Down
82 changes: 46 additions & 36 deletions compiler/rustc_span/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -715,12 +715,17 @@ impl Span {
(!ctxt.is_root()).then(|| ctxt.outer_expn_data().call_site)
}

/// Walk down the expansion ancestors to find a span that's contained within `outer`.
/// Find the first ancestor span that's contained within `outer`.
///
/// The span returned by this method may have a different [`SyntaxContext`] as `outer`.
/// This method traverses the macro expansion ancestors until it finds the first span
/// that's contained within `outer`.
///
/// The span returned by this method may have a different [`SyntaxContext`] than `outer`.
/// If you need to extend the span, use [`find_ancestor_inside_same_ctxt`] instead,
/// because joining spans with different syntax contexts can create unexpected results.
///
/// This is used to find the span of the macro call when a parent expr span, i.e. `outer`, is known.
///
/// [`find_ancestor_inside_same_ctxt`]: Self::find_ancestor_inside_same_ctxt
pub fn find_ancestor_inside(mut self, outer: Span) -> Option<Span> {
while !outer.contains(self) {
Expand All @@ -729,8 +734,10 @@ impl Span {
Some(self)
}

/// Walk down the expansion ancestors to find a span with the same [`SyntaxContext`] as
/// `other`.
/// Find the first ancestor span with the same [`SyntaxContext`] as `other`.
///
/// This method traverses the macro expansion ancestors until it finds a span
/// that has the same [`SyntaxContext`] as `other`.
///
/// Like [`find_ancestor_inside_same_ctxt`], but specifically for when spans might not
/// overlap. Take care when using this, and prefer [`find_ancestor_inside`] or
Expand All @@ -746,9 +753,12 @@ impl Span {
Some(self)
}

/// Walk down the expansion ancestors to find a span that's contained within `outer` and
/// Find the first ancestor span that's contained within `outer` and
/// has the same [`SyntaxContext`] as `outer`.
///
/// This method traverses the macro expansion ancestors until it finds a span
/// that is both contained within `outer` and has the same [`SyntaxContext`] as `outer`.
///
/// This method is the combination of [`find_ancestor_inside`] and
/// [`find_ancestor_in_same_ctxt`] and should be preferred when extending the returned span.
/// If you do not need to modify the span, use [`find_ancestor_inside`] instead.
Expand All @@ -762,43 +772,43 @@ impl Span {
Some(self)
}

/// Recursively walk down the expansion ancestors to find the oldest ancestor span with the same
/// [`SyntaxContext`] the initial span.
/// Find the first ancestor span that does not come from an external macro.
///
/// 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
/// This method traverses the macro expansion ancestors until it finds a span
/// that is either from user-written code or from a local macro (defined in the current crate).
///
/// ```ignore (illustrative example, contains type error)
/// macro_rules! outer {
/// ($x: expr) => {
/// inner!($x)
/// }
/// }
/// External macros are those defined in dependencies or the standard library.
/// This method is useful for reporting errors in user-controllable code and avoiding
/// diagnostics inside external macros.
///
/// macro_rules! inner {
/// ($x: expr) => {
/// format!("error: {}", $x)
/// //~^ ERROR mismatched types
/// }
/// }
/// # See also
///
/// fn bar(x: &str) -> Result<(), Box<dyn std::error::Error>> {
/// Err(outer!(x))
/// }
/// ```
/// - [`Self::find_ancestor_not_from_macro`]
/// - [`Self::in_external_macro`]
pub fn find_ancestor_not_from_extern_macro(mut self, sm: &SourceMap) -> Option<Span> {
while self.in_external_macro(sm) {
self = self.parent_callsite()?;
}
Some(self)
}

/// Find the first ancestor span that does not come from any macro expansion.
///
/// 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
/// oldest ancestor span that is both still local and shares the same [`SyntaxContext`] as the
/// initial span.
pub fn find_oldest_ancestor_in_same_ctxt(self) -> Span {
let mut cur = self;
while cur.eq_ctxt(self)
&& let Some(parent_callsite) = cur.parent_callsite()
{
cur = parent_callsite;
/// This method traverses the macro expansion ancestors until it finds a span
/// that originates from user-written code rather than any macro-generated code.
///
/// This method is useful for reporting errors at the exact location users wrote code
/// and providing suggestions at directly editable locations.
///
/// # See also
///
/// - [`Self::find_ancestor_not_from_extern_macro`]
/// - [`Span::from_expansion`]
pub fn find_ancestor_not_from_macro(mut self) -> Option<Span> {
while self.from_expansion() {
self = self.parent_callsite()?;
}
cur
Some(self)
}

/// Edition of the crate from which this span came.
Expand Down
Loading