Skip to content

Commit 962b219

Browse files
committed
Annotate derived spans and move span suggestion code
* Annotate `derive`d spans from the user's code with the appropciate context * Add `Span::can_be_used_for_suggestion` to query if the underlying span at the users' code
1 parent 8bee2b8 commit 962b219

File tree

8 files changed

+112
-90
lines changed

8 files changed

+112
-90
lines changed

compiler/rustc_builtin_macros/src/deriving/generic/mod.rs

+74-61
Large diffs are not rendered by default.

compiler/rustc_hir/src/hir.rs

+16-15
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use rustc_index::vec::IndexVec;
1717
use rustc_macros::HashStable_Generic;
1818
use rustc_span::source_map::Spanned;
1919
use rustc_span::symbol::{kw, sym, Ident, Symbol};
20-
use rustc_span::{def_id::LocalDefId, BytePos, ExpnKind, MacroKind, MultiSpan, Span, DUMMY_SP};
20+
use rustc_span::{def_id::LocalDefId, BytePos, MultiSpan, Span, DUMMY_SP};
2121
use rustc_target::asm::InlineAsmRegOrRegClass;
2222
use rustc_target::spec::abi::Abi;
2323

@@ -525,20 +525,21 @@ pub struct GenericParam<'hir> {
525525
}
526526

527527
impl GenericParam<'hir> {
528-
pub fn bounds_span(&self) -> Option<Span> {
529-
self.bounds.iter().fold(None, |span, bound| {
530-
if let ExpnKind::Macro(MacroKind::Derive, _) =
531-
bound.span().ctxt().outer_expn_data().kind
532-
{
533-
// We ignore bounds that come from exclusively from a `#[derive(_)]`, because we
534-
// can't really point at them, and we sometimes use this method to get a span
535-
// appropriate for suggestions.
536-
span
537-
} else {
538-
let span = span.map(|s| s.to(bound.span())).unwrap_or_else(|| bound.span());
539-
Some(span)
540-
}
541-
})
528+
pub fn bounds_span_for_suggestions(&self) -> Option<Span> {
529+
self.bounds
530+
.iter()
531+
.fold(None, |span: Option<Span>, bound| {
532+
// We include bounds that come from a `#[derive(_)]` but point at the user's code,
533+
// as we use this method to get a span appropriate for suggestions.
534+
// FIXME: a more "principled" approach should be taken here.
535+
if !bound.span().can_be_used_for_suggestions() {
536+
None
537+
} else {
538+
let span = span.map(|s| s.to(bound.span())).unwrap_or_else(|| bound.span());
539+
Some(span)
540+
}
541+
})
542+
.map(|sp| sp.shrink_to_hi())
542543
}
543544
}
544545

compiler/rustc_middle/src/ty/diagnostics.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ pub fn suggest_constraining_type_param(
270270
// `where` clause instead of `trait Base<T: Copy = String>: Super<T>`.
271271
&& !matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. })
272272
{
273-
if let Some(bounds_span) = param.bounds_span() {
273+
if let Some(span) = param.bounds_span_for_suggestions() {
274274
// If user has provided some bounds, suggest restricting them:
275275
//
276276
// fn foo<T: Foo>(t: T) { ... }
@@ -284,7 +284,7 @@ pub fn suggest_constraining_type_param(
284284
// --
285285
// |
286286
// replace with: `T: Bar +`
287-
suggest_restrict(bounds_span.shrink_to_hi());
287+
suggest_restrict(span);
288288
} else {
289289
// If user hasn't provided any bounds, suggest adding a new one:
290290
//

compiler/rustc_resolve/src/late/diagnostics.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1735,7 +1735,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
17351735
(generics.span, format!("<{}>", ident))
17361736
};
17371737
// Do not suggest if this is coming from macro expansion.
1738-
if !span.from_expansion() {
1738+
if span.can_be_used_for_suggestions() {
17391739
return Some((
17401740
span.shrink_to_hi(),
17411741
msg,
@@ -1825,7 +1825,7 @@ impl<'tcx> LifetimeContext<'_, 'tcx> {
18251825
continue;
18261826
}
18271827
suggested_spans.push(span);
1828-
if !span.from_expansion() {
1828+
if span.can_be_used_for_suggestions() {
18291829
err.span_suggestion(
18301830
span,
18311831
&format!("consider introducing lifetime `{}` here", lifetime_ref),

compiler/rustc_span/src/lib.rs

+10
Original file line numberDiff line numberDiff line change
@@ -551,6 +551,16 @@ impl Span {
551551
matches!(self.ctxt().outer_expn_data().kind, ExpnKind::Macro(MacroKind::Derive, _))
552552
}
553553

554+
/// Gate suggestions that would not be appropriate in a context the user didn't write.
555+
pub fn can_be_used_for_suggestions(self) -> bool {
556+
!self.from_expansion()
557+
// FIXME: If this span comes from a `derive` macro but it points at code the user wrote,
558+
// the callsite span and the span will be pointing at different places. It also means that
559+
// we can safely provide suggestions on this span.
560+
|| (matches!(self.ctxt().outer_expn_data().kind, ExpnKind::Macro(MacroKind::Derive, _))
561+
&& self.parent_callsite().map(|p| (p.lo(), p.hi())) != Some((self.lo(), self.hi())))
562+
}
563+
554564
#[inline]
555565
pub fn with_root_ctxt(lo: BytePos, hi: BytePos) -> Span {
556566
Span::new(lo, hi, SyntaxContext::root(), None)

compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -262,16 +262,16 @@ fn suggest_restriction(
262262
match generics
263263
.params
264264
.iter()
265-
.map(|p| p.bounds_span().unwrap_or(p.span))
266-
.filter(|&span| generics.span.contains(span) && span.desugaring_kind().is_none())
265+
.map(|p| p.bounds_span_for_suggestions().unwrap_or(p.span.shrink_to_hi()))
266+
.filter(|&span| generics.span.contains(span) && span.can_be_used_for_suggestions())
267267
.max_by_key(|span| span.hi())
268268
{
269269
// `fn foo(t: impl Trait)`
270270
// ^ suggest `<T: Trait>` here
271271
None => (generics.span, format!("<{}>", type_param)),
272272
// `fn foo<A>(t: impl Trait)`
273273
// ^^^ suggest `<A, T: Trait>` here
274-
Some(span) => (span.shrink_to_hi(), format!(", {}", type_param)),
274+
Some(span) => (span, format!(", {}", type_param)),
275275
},
276276
// `fn foo(t: impl Trait)`
277277
// ^ suggest `where <T as Trait>::A: Bound`

compiler/rustc_typeck/src/collect.rs

+3-5
Original file line numberDiff line numberDiff line change
@@ -177,11 +177,9 @@ crate fn placeholder_type_error(
177177
sugg.push((arg.span, (*type_name).to_string()));
178178
} else {
179179
let last = generics.iter().last().unwrap();
180-
sugg.push((
181-
// Account for bounds, we want `fn foo<T: E, K>(_: K)` not `fn foo<T, K: E>(_: K)`.
182-
last.bounds_span().unwrap_or(last.span).shrink_to_hi(),
183-
format!(", {}", type_name),
184-
));
180+
// Account for bounds, we want `fn foo<T: E, K>(_: K)` not `fn foo<T, K: E>(_: K)`.
181+
let span = last.bounds_span_for_suggestions().unwrap_or(last.span.shrink_to_hi());
182+
sugg.push((span, format!(", {}", type_name)));
185183
}
186184

187185
let mut err = bad_placeholder_type(tcx, placeholder_types, kind);

src/test/ui/issues/issue-38821.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ LL | impl<T: NotNull> IntoNullable for T {
1212
= note: this error originates in the derive macro `Copy` (in Nightly builds, run with -Z macro-backtrace for more info)
1313
help: consider further restricting the associated type
1414
|
15-
LL | Expr: Expression<SqlType=<Col::SqlType as IntoNullable>::Nullable>, <Col as Expression>::SqlType: NotNull
16-
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
15+
LL | Expr: Expression<SqlType=<Col::SqlType as IntoNullable>::Nullable>, <Col as Expression>::SqlType: NotNull,
16+
| +++++++++++++++++++++++++++++++++++++++
1717

1818
error: aborting due to previous error
1919

0 commit comments

Comments
 (0)