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
This commit is contained in:
Esteban Kuber 2021-11-20 18:46:36 +00:00
parent 8bee2b88c0
commit 962b2197a5
8 changed files with 112 additions and 90 deletions

View File

@ -570,7 +570,8 @@ impl<'a> TraitDef<'a> {
let Generics { mut params, mut where_clause, .. } = let Generics { mut params, mut where_clause, .. } =
self.generics.to_generics(cx, self.span, type_ident, generics); self.generics.to_generics(cx, self.span, type_ident, generics);
where_clause.span = generics.where_clause.span; where_clause.span = generics.where_clause.span;
let span = generics.span; let ctxt = self.span.ctxt();
let span = generics.span.with_ctxt(ctxt);
// Create the generic parameters // Create the generic parameters
params.extend(generics.params.iter().map(|param| match &param.kind { params.extend(generics.params.iter().map(|param| match &param.kind {
@ -591,12 +592,12 @@ impl<'a> TraitDef<'a> {
param.bounds.iter().cloned() param.bounds.iter().cloned()
).collect(); ).collect();
cx.typaram(param.ident.span, param.ident, vec![], bounds, None) cx.typaram(param.ident.span.with_ctxt(ctxt), param.ident, vec![], bounds, None)
} }
GenericParamKind::Const { ty, kw_span, .. } => { GenericParamKind::Const { ty, kw_span, .. } => {
let const_nodefault_kind = GenericParamKind::Const { let const_nodefault_kind = GenericParamKind::Const {
ty: ty.clone(), ty: ty.clone(),
kw_span: *kw_span, kw_span: kw_span.with_ctxt(ctxt),
// We can't have default values inside impl block // We can't have default values inside impl block
default: None, default: None,
@ -610,11 +611,25 @@ impl<'a> TraitDef<'a> {
// and similarly for where clauses // and similarly for where clauses
where_clause.predicates.extend(generics.where_clause.predicates.iter().map(|clause| { where_clause.predicates.extend(generics.where_clause.predicates.iter().map(|clause| {
match clause { match clause {
ast::WherePredicate::BoundPredicate(_) ast::WherePredicate::BoundPredicate(wb) => {
| ast::WherePredicate::RegionPredicate(_) => clause.clone(), let span = wb.span.with_ctxt(ctxt);
ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate {
span,
..wb.clone()
})
}
ast::WherePredicate::RegionPredicate(wr) => {
let span = wr.span.with_ctxt(ctxt);
ast::WherePredicate::RegionPredicate(ast::WhereRegionPredicate {
span,
..wr.clone()
})
}
ast::WherePredicate::EqPredicate(we) => { ast::WherePredicate::EqPredicate(we) => {
let span = we.span.with_ctxt(ctxt);
ast::WherePredicate::EqPredicate(ast::WhereEqPredicate { ast::WherePredicate::EqPredicate(ast::WhereEqPredicate {
id: ast::DUMMY_NODE_ID, id: ast::DUMMY_NODE_ID,
span,
..we.clone() ..we.clone()
}) })
} }
@ -678,13 +693,13 @@ impl<'a> TraitDef<'a> {
.iter() .iter()
.map(|param| match param.kind { .map(|param| match param.kind {
GenericParamKind::Lifetime { .. } => { GenericParamKind::Lifetime { .. } => {
GenericArg::Lifetime(cx.lifetime(param.ident.span, param.ident)) GenericArg::Lifetime(cx.lifetime(param.ident.span.with_ctxt(ctxt), param.ident))
} }
GenericParamKind::Type { .. } => { GenericParamKind::Type { .. } => {
GenericArg::Type(cx.ty_ident(param.ident.span, param.ident)) GenericArg::Type(cx.ty_ident(param.ident.span.with_ctxt(ctxt), param.ident))
} }
GenericParamKind::Const { .. } => { GenericParamKind::Const { .. } => {
GenericArg::Const(cx.const_ident(param.ident.span, param.ident)) GenericArg::Const(cx.const_ident(param.ident.span.with_ctxt(ctxt), param.ident))
} }
}) })
.collect(); .collect();
@ -832,16 +847,17 @@ impl<'a> MethodDef<'a> {
nonself_args: &[P<Expr>], nonself_args: &[P<Expr>],
fields: &SubstructureFields<'_>, fields: &SubstructureFields<'_>,
) -> P<Expr> { ) -> P<Expr> {
let span = trait_.span;
let substructure = Substructure { let substructure = Substructure {
type_ident, type_ident,
method_ident: Ident::new(self.name, trait_.span), method_ident: Ident::new(self.name, span),
self_args, self_args,
nonself_args, nonself_args,
fields, fields,
}; };
let mut f = self.combine_substructure.borrow_mut(); let mut f = self.combine_substructure.borrow_mut();
let f: &mut CombineSubstructureFunc<'_> = &mut *f; let f: &mut CombineSubstructureFunc<'_> = &mut *f;
f(cx, trait_.span, &substructure) f(cx, span, &substructure)
} }
fn get_ret_ty( fn get_ret_ty(
@ -869,9 +885,10 @@ impl<'a> MethodDef<'a> {
let mut nonself_args = Vec::new(); let mut nonself_args = Vec::new();
let mut arg_tys = Vec::new(); let mut arg_tys = Vec::new();
let mut nonstatic = false; let mut nonstatic = false;
let span = trait_.span;
let ast_explicit_self = self.explicit_self.as_ref().map(|self_ptr| { let ast_explicit_self = self.explicit_self.as_ref().map(|self_ptr| {
let (self_expr, explicit_self) = ty::get_explicit_self(cx, trait_.span, self_ptr); let (self_expr, explicit_self) = ty::get_explicit_self(cx, span, self_ptr);
self_args.push(self_expr); self_args.push(self_expr);
nonstatic = true; nonstatic = true;
@ -880,11 +897,11 @@ impl<'a> MethodDef<'a> {
}); });
for (ty, name) in self.args.iter() { for (ty, name) in self.args.iter() {
let ast_ty = ty.to_ty(cx, trait_.span, type_ident, generics); let ast_ty = ty.to_ty(cx, span, type_ident, generics);
let ident = Ident::new(*name, trait_.span); let ident = Ident::new(*name, span);
arg_tys.push((ident, ast_ty)); arg_tys.push((ident, ast_ty));
let arg_expr = cx.expr_ident(trait_.span, ident); let arg_expr = cx.expr_ident(span, ident);
match *ty { match *ty {
// for static methods, just treat any Self // for static methods, just treat any Self
@ -893,7 +910,7 @@ impl<'a> MethodDef<'a> {
self_args.push(arg_expr); self_args.push(arg_expr);
} }
Ptr(ref ty, _) if matches!(**ty, Self_) && nonstatic => { Ptr(ref ty, _) if matches!(**ty, Self_) && nonstatic => {
self_args.push(cx.expr_deref(trait_.span, arg_expr)) self_args.push(cx.expr_deref(span, arg_expr))
} }
_ => { _ => {
nonself_args.push(arg_expr); nonself_args.push(arg_expr);
@ -914,33 +931,33 @@ impl<'a> MethodDef<'a> {
arg_types: Vec<(Ident, P<ast::Ty>)>, arg_types: Vec<(Ident, P<ast::Ty>)>,
body: P<Expr>, body: P<Expr>,
) -> P<ast::AssocItem> { ) -> P<ast::AssocItem> {
let span = trait_.span;
// Create the generics that aren't for `Self`. // Create the generics that aren't for `Self`.
let fn_generics = self.generics.to_generics(cx, trait_.span, type_ident, generics); let fn_generics = self.generics.to_generics(cx, span, type_ident, generics);
let args = { let args = {
let self_args = explicit_self.map(|explicit_self| { let self_args = explicit_self.map(|explicit_self| {
let ident = Ident::with_dummy_span(kw::SelfLower).with_span_pos(trait_.span); let ident = Ident::with_dummy_span(kw::SelfLower).with_span_pos(span);
ast::Param::from_self(ast::AttrVec::default(), explicit_self, ident) ast::Param::from_self(ast::AttrVec::default(), explicit_self, ident)
}); });
let nonself_args = let nonself_args = arg_types.into_iter().map(|(name, ty)| cx.param(span, name, ty));
arg_types.into_iter().map(|(name, ty)| cx.param(trait_.span, name, ty));
self_args.into_iter().chain(nonself_args).collect() self_args.into_iter().chain(nonself_args).collect()
}; };
let ret_type = self.get_ret_ty(cx, trait_, generics, type_ident); let ret_type = self.get_ret_ty(cx, trait_, generics, type_ident);
let method_ident = Ident::new(self.name, trait_.span); let method_ident = Ident::new(self.name, span);
let fn_decl = cx.fn_decl(args, ast::FnRetTy::Ty(ret_type)); let fn_decl = cx.fn_decl(args, ast::FnRetTy::Ty(ret_type));
let body_block = cx.block_expr(body); let body_block = cx.block_expr(body);
let unsafety = if self.is_unsafe { ast::Unsafe::Yes(trait_.span) } else { ast::Unsafe::No }; let unsafety = if self.is_unsafe { ast::Unsafe::Yes(span) } else { ast::Unsafe::No };
let trait_lo_sp = trait_.span.shrink_to_lo(); let trait_lo_sp = span.shrink_to_lo();
let sig = ast::FnSig { let sig = ast::FnSig {
header: ast::FnHeader { unsafety, ext: ast::Extern::None, ..ast::FnHeader::default() }, header: ast::FnHeader { unsafety, ext: ast::Extern::None, ..ast::FnHeader::default() },
decl: fn_decl, decl: fn_decl,
span: trait_.span, span,
}; };
let defaultness = ast::Defaultness::Final; let defaultness = ast::Defaultness::Final;
@ -948,7 +965,7 @@ impl<'a> MethodDef<'a> {
P(ast::AssocItem { P(ast::AssocItem {
id: ast::DUMMY_NODE_ID, id: ast::DUMMY_NODE_ID,
attrs: self.attributes.clone(), attrs: self.attributes.clone(),
span: trait_.span, span,
vis: ast::Visibility { vis: ast::Visibility {
span: trait_lo_sp, span: trait_lo_sp,
kind: ast::VisibilityKind::Inherited, kind: ast::VisibilityKind::Inherited,
@ -1011,11 +1028,11 @@ impl<'a> MethodDef<'a> {
nonself_args: &[P<Expr>], nonself_args: &[P<Expr>],
use_temporaries: bool, use_temporaries: bool,
) -> P<Expr> { ) -> P<Expr> {
let mut raw_fields = Vec::new(); // Vec<[fields of self], let mut raw_fields = Vec::new(); // Vec<[fields of self], [fields of next Self arg], [etc]>
// [fields of next Self arg], [etc]> let span = trait_.span;
let mut patterns = Vec::new(); let mut patterns = Vec::new();
for i in 0..self_args.len() { for i in 0..self_args.len() {
let struct_path = cx.path(trait_.span, vec![type_ident]); let struct_path = cx.path(span, vec![type_ident]);
let (pat, ident_expr) = trait_.create_struct_pattern( let (pat, ident_expr) = trait_.create_struct_pattern(
cx, cx,
struct_path, struct_path,
@ -1035,7 +1052,7 @@ impl<'a> MethodDef<'a> {
let mut other_fields: Vec<vec::IntoIter<_>> = raw_fields.collect(); let mut other_fields: Vec<vec::IntoIter<_>> = raw_fields.collect();
first_field first_field
.map(|(span, opt_id, field, attrs)| FieldInfo { .map(|(span, opt_id, field, attrs)| FieldInfo {
span, span: span.with_ctxt(trait_.span.ctxt()),
name: opt_id, name: opt_id,
self_: field, self_: field,
other: other_fields other: other_fields
@ -1049,7 +1066,7 @@ impl<'a> MethodDef<'a> {
}) })
.collect() .collect()
} else { } else {
cx.span_bug(trait_.span, "no `self` parameter for method in generic `derive`") cx.span_bug(span, "no `self` parameter for method in generic `derive`")
}; };
// body of the inner most destructuring match // body of the inner most destructuring match
@ -1066,11 +1083,7 @@ impl<'a> MethodDef<'a> {
// structs. This is actually right-to-left, but it shouldn't // structs. This is actually right-to-left, but it shouldn't
// matter. // matter.
for (arg_expr, pat) in iter::zip(self_args, patterns) { for (arg_expr, pat) in iter::zip(self_args, patterns) {
body = cx.expr_match( body = cx.expr_match(span, arg_expr.clone(), vec![cx.arm(span, pat.clone(), body)])
trait_.span,
arg_expr.clone(),
vec![cx.arm(trait_.span, pat.clone(), body)],
)
} }
body body
@ -1180,7 +1193,7 @@ impl<'a> MethodDef<'a> {
mut self_args: Vec<P<Expr>>, mut self_args: Vec<P<Expr>>,
nonself_args: &[P<Expr>], nonself_args: &[P<Expr>],
) -> P<Expr> { ) -> P<Expr> {
let sp = trait_.span; let span = trait_.span;
let variants = &enum_def.variants; let variants = &enum_def.variants;
let self_arg_names = iter::once("__self".to_string()) let self_arg_names = iter::once("__self".to_string())
@ -1195,7 +1208,7 @@ impl<'a> MethodDef<'a> {
let self_arg_idents = self_arg_names let self_arg_idents = self_arg_names
.iter() .iter()
.map(|name| Ident::from_str_and_span(name, sp)) .map(|name| Ident::from_str_and_span(name, span))
.collect::<Vec<Ident>>(); .collect::<Vec<Ident>>();
// The `vi_idents` will be bound, solely in the catch-all, to // The `vi_idents` will be bound, solely in the catch-all, to
@ -1205,7 +1218,7 @@ impl<'a> MethodDef<'a> {
.iter() .iter()
.map(|name| { .map(|name| {
let vi_suffix = format!("{}_vi", &name[..]); let vi_suffix = format!("{}_vi", &name[..]);
Ident::from_str_and_span(&vi_suffix, trait_.span) Ident::from_str_and_span(&vi_suffix, span)
}) })
.collect::<Vec<Ident>>(); .collect::<Vec<Ident>>();
@ -1235,7 +1248,7 @@ impl<'a> MethodDef<'a> {
self_arg_name, self_arg_name,
ast::Mutability::Not, ast::Mutability::Not,
); );
(cx.pat(sp, PatKind::Ref(p, ast::Mutability::Not)), idents) (cx.pat(span, PatKind::Ref(p, ast::Mutability::Not)), idents)
}; };
// A single arm has form (&VariantK, &VariantK, ...) => BodyK // A single arm has form (&VariantK, &VariantK, ...) => BodyK
@ -1254,7 +1267,7 @@ impl<'a> MethodDef<'a> {
} }
// Here is the pat = `(&VariantK, &VariantK, ...)` // Here is the pat = `(&VariantK, &VariantK, ...)`
let single_pat = cx.pat_tuple(sp, subpats); let single_pat = cx.pat_tuple(span, subpats);
// For the BodyK, we need to delegate to our caller, // For the BodyK, we need to delegate to our caller,
// passing it an EnumMatching to indicate which case // passing it an EnumMatching to indicate which case
@ -1271,7 +1284,7 @@ impl<'a> MethodDef<'a> {
.into_iter() .into_iter()
.enumerate() .enumerate()
// For each arg field of self, pull out its getter expr ... // For each arg field of self, pull out its getter expr ...
.map(|(field_index, (sp, opt_ident, self_getter_expr, attrs))| { .map(|(field_index, (span, opt_ident, self_getter_expr, attrs))| {
// ... but FieldInfo also wants getter expr // ... but FieldInfo also wants getter expr
// for matching other arguments of Self type; // for matching other arguments of Self type;
// so walk across the *other* self_pats_idents // so walk across the *other* self_pats_idents
@ -1294,7 +1307,7 @@ impl<'a> MethodDef<'a> {
.collect::<Vec<P<Expr>>>(); .collect::<Vec<P<Expr>>>();
FieldInfo { FieldInfo {
span: sp, span,
name: opt_ident, name: opt_ident,
self_: self_getter_expr, self_: self_getter_expr,
other: others, other: others,
@ -1317,7 +1330,7 @@ impl<'a> MethodDef<'a> {
&substructure, &substructure,
); );
cx.arm(sp, single_pat, arm_expr) cx.arm(span, single_pat, arm_expr)
}) })
.collect(); .collect();
@ -1340,12 +1353,12 @@ impl<'a> MethodDef<'a> {
// Since we know that all the arguments will match if we reach // Since we know that all the arguments will match if we reach
// the match expression we add the unreachable intrinsics as the // the match expression we add the unreachable intrinsics as the
// result of the catch all which should help llvm in optimizing it // result of the catch all which should help llvm in optimizing it
Some(deriving::call_unreachable(cx, sp)) Some(deriving::call_unreachable(cx, span))
} }
_ => None, _ => None,
}; };
if let Some(arm) = default { if let Some(arm) = default {
match_arms.push(cx.arm(sp, cx.pat_wild(sp), arm)); match_arms.push(cx.arm(span, cx.pat_wild(span), arm));
} }
// We will usually need the catch-all after matching the // We will usually need the catch-all after matching the
@ -1379,23 +1392,23 @@ impl<'a> MethodDef<'a> {
// We also build an expression which checks whether all discriminants are equal // We also build an expression which checks whether all discriminants are equal
// discriminant_test = __self0_vi == __self1_vi && __self0_vi == __self2_vi && ... // discriminant_test = __self0_vi == __self1_vi && __self0_vi == __self2_vi && ...
let mut discriminant_test = cx.expr_bool(sp, true); let mut discriminant_test = cx.expr_bool(span, true);
let mut first_ident = None; let mut first_ident = None;
for (&ident, self_arg) in iter::zip(&vi_idents, &self_args) { for (&ident, self_arg) in iter::zip(&vi_idents, &self_args) {
let self_addr = cx.expr_addr_of(sp, self_arg.clone()); let self_addr = cx.expr_addr_of(span, self_arg.clone());
let variant_value = let variant_value =
deriving::call_intrinsic(cx, sp, sym::discriminant_value, vec![self_addr]); deriving::call_intrinsic(cx, span, sym::discriminant_value, vec![self_addr]);
let let_stmt = cx.stmt_let(sp, false, ident, variant_value); let let_stmt = cx.stmt_let(span, false, ident, variant_value);
index_let_stmts.push(let_stmt); index_let_stmts.push(let_stmt);
match first_ident { match first_ident {
Some(first) => { Some(first) => {
let first_expr = cx.expr_ident(sp, first); let first_expr = cx.expr_ident(span, first);
let id = cx.expr_ident(sp, ident); let id = cx.expr_ident(span, ident);
let test = cx.expr_binary(sp, BinOpKind::Eq, first_expr, id); let test = cx.expr_binary(span, BinOpKind::Eq, first_expr, id);
discriminant_test = discriminant_test =
cx.expr_binary(sp, BinOpKind::And, discriminant_test, test) cx.expr_binary(span, BinOpKind::And, discriminant_test, test)
} }
None => { None => {
first_ident = Some(ident); first_ident = Some(ident);
@ -1417,8 +1430,8 @@ impl<'a> MethodDef<'a> {
// them when they are fed as r-values into a tuple // them when they are fed as r-values into a tuple
// expression; here add a layer of borrowing, turning // expression; here add a layer of borrowing, turning
// `(*self, *__arg_0, ...)` into `(&*self, &*__arg_0, ...)`. // `(*self, *__arg_0, ...)` into `(&*self, &*__arg_0, ...)`.
self_args.map_in_place(|self_arg| cx.expr_addr_of(sp, self_arg)); self_args.map_in_place(|self_arg| cx.expr_addr_of(span, self_arg));
let match_arg = cx.expr(sp, ast::ExprKind::Tup(self_args)); let match_arg = cx.expr(span, ast::ExprKind::Tup(self_args));
// Lastly we create an expression which branches on all discriminants being equal // Lastly we create an expression which branches on all discriminants being equal
// if discriminant_test { // if discriminant_test {
@ -1432,10 +1445,10 @@ impl<'a> MethodDef<'a> {
// else { // else {
// <delegated expression referring to __self0_vi, et al.> // <delegated expression referring to __self0_vi, et al.>
// } // }
let all_match = cx.expr_match(sp, match_arg, match_arms); let all_match = cx.expr_match(span, match_arg, match_arms);
let arm_expr = cx.expr_if(sp, discriminant_test, all_match, Some(arm_expr)); let arm_expr = cx.expr_if(span, discriminant_test, all_match, Some(arm_expr));
index_let_stmts.push(cx.stmt_expr(arm_expr)); index_let_stmts.push(cx.stmt_expr(arm_expr));
cx.expr_block(cx.block(sp, index_let_stmts)) cx.expr_block(cx.block(span, index_let_stmts))
} else if variants.is_empty() { } else if variants.is_empty() {
// As an additional wrinkle, For a zero-variant enum A, // As an additional wrinkle, For a zero-variant enum A,
// currently the compiler // currently the compiler
@ -1486,16 +1499,16 @@ impl<'a> MethodDef<'a> {
// derive Debug on such a type could here generate code // derive Debug on such a type could here generate code
// that needs the feature gate enabled.) // that needs the feature gate enabled.)
deriving::call_unreachable(cx, sp) deriving::call_unreachable(cx, span)
} else { } else {
// Final wrinkle: the self_args are expressions that deref // Final wrinkle: the self_args are expressions that deref
// down to desired places, but we cannot actually deref // down to desired places, but we cannot actually deref
// them when they are fed as r-values into a tuple // them when they are fed as r-values into a tuple
// expression; here add a layer of borrowing, turning // expression; here add a layer of borrowing, turning
// `(*self, *__arg_0, ...)` into `(&*self, &*__arg_0, ...)`. // `(*self, *__arg_0, ...)` into `(&*self, &*__arg_0, ...)`.
self_args.map_in_place(|self_arg| cx.expr_addr_of(sp, self_arg)); self_args.map_in_place(|self_arg| cx.expr_addr_of(span, self_arg));
let match_arg = cx.expr(sp, ast::ExprKind::Tup(self_args)); let match_arg = cx.expr(span, ast::ExprKind::Tup(self_args));
cx.expr_match(sp, match_arg, match_arms) cx.expr_match(span, match_arg, match_arms)
} }
} }

View File

@ -17,7 +17,7 @@ use rustc_index::vec::IndexVec;
use rustc_macros::HashStable_Generic; use rustc_macros::HashStable_Generic;
use rustc_span::source_map::Spanned; use rustc_span::source_map::Spanned;
use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::{def_id::LocalDefId, BytePos, ExpnKind, MacroKind, MultiSpan, Span, DUMMY_SP}; use rustc_span::{def_id::LocalDefId, BytePos, MultiSpan, Span, DUMMY_SP};
use rustc_target::asm::InlineAsmRegOrRegClass; use rustc_target::asm::InlineAsmRegOrRegClass;
use rustc_target::spec::abi::Abi; use rustc_target::spec::abi::Abi;
@ -525,20 +525,21 @@ pub struct GenericParam<'hir> {
} }
impl GenericParam<'hir> { impl GenericParam<'hir> {
pub fn bounds_span(&self) -> Option<Span> { pub fn bounds_span_for_suggestions(&self) -> Option<Span> {
self.bounds.iter().fold(None, |span, bound| { self.bounds
if let ExpnKind::Macro(MacroKind::Derive, _) = .iter()
bound.span().ctxt().outer_expn_data().kind .fold(None, |span: Option<Span>, bound| {
{ // We include bounds that come from a `#[derive(_)]` but point at the user's code,
// We ignore bounds that come from exclusively from a `#[derive(_)]`, because we // as we use this method to get a span appropriate for suggestions.
// can't really point at them, and we sometimes use this method to get a span // FIXME: a more "principled" approach should be taken here.
// appropriate for suggestions. if !bound.span().can_be_used_for_suggestions() {
span None
} else { } else {
let span = span.map(|s| s.to(bound.span())).unwrap_or_else(|| bound.span()); let span = span.map(|s| s.to(bound.span())).unwrap_or_else(|| bound.span());
Some(span) Some(span)
} }
}) })
.map(|sp| sp.shrink_to_hi())
} }
} }

View File

@ -270,7 +270,7 @@ pub fn suggest_constraining_type_param(
// `where` clause instead of `trait Base<T: Copy = String>: Super<T>`. // `where` clause instead of `trait Base<T: Copy = String>: Super<T>`.
&& !matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. }) && !matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. })
{ {
if let Some(bounds_span) = param.bounds_span() { if let Some(span) = param.bounds_span_for_suggestions() {
// If user has provided some bounds, suggest restricting them: // If user has provided some bounds, suggest restricting them:
// //
// fn foo<T: Foo>(t: T) { ... } // fn foo<T: Foo>(t: T) { ... }
@ -284,7 +284,7 @@ pub fn suggest_constraining_type_param(
// -- // --
// | // |
// replace with: `T: Bar +` // replace with: `T: Bar +`
suggest_restrict(bounds_span.shrink_to_hi()); suggest_restrict(span);
} else { } else {
// If user hasn't provided any bounds, suggest adding a new one: // If user hasn't provided any bounds, suggest adding a new one:
// //

View File

@ -1735,7 +1735,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
(generics.span, format!("<{}>", ident)) (generics.span, format!("<{}>", ident))
}; };
// Do not suggest if this is coming from macro expansion. // Do not suggest if this is coming from macro expansion.
if !span.from_expansion() { if span.can_be_used_for_suggestions() {
return Some(( return Some((
span.shrink_to_hi(), span.shrink_to_hi(),
msg, msg,
@ -1825,7 +1825,7 @@ impl<'tcx> LifetimeContext<'_, 'tcx> {
continue; continue;
} }
suggested_spans.push(span); suggested_spans.push(span);
if !span.from_expansion() { if span.can_be_used_for_suggestions() {
err.span_suggestion( err.span_suggestion(
span, span,
&format!("consider introducing lifetime `{}` here", lifetime_ref), &format!("consider introducing lifetime `{}` here", lifetime_ref),

View File

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

View File

@ -262,8 +262,8 @@ fn suggest_restriction(
match generics match generics
.params .params
.iter() .iter()
.map(|p| p.bounds_span().unwrap_or(p.span)) .map(|p| p.bounds_span_for_suggestions().unwrap_or(p.span.shrink_to_hi()))
.filter(|&span| generics.span.contains(span) && span.desugaring_kind().is_none()) .filter(|&span| generics.span.contains(span) && span.can_be_used_for_suggestions())
.max_by_key(|span| span.hi()) .max_by_key(|span| span.hi())
{ {
// `fn foo(t: impl Trait)` // `fn foo(t: impl Trait)`
@ -271,7 +271,7 @@ fn suggest_restriction(
None => (generics.span, format!("<{}>", type_param)), None => (generics.span, format!("<{}>", type_param)),
// `fn foo<A>(t: impl Trait)` // `fn foo<A>(t: impl Trait)`
// ^^^ suggest `<A, T: Trait>` here // ^^^ suggest `<A, T: Trait>` here
Some(span) => (span.shrink_to_hi(), format!(", {}", type_param)), Some(span) => (span, format!(", {}", type_param)),
}, },
// `fn foo(t: impl Trait)` // `fn foo(t: impl Trait)`
// ^ suggest `where <T as Trait>::A: Bound` // ^ suggest `where <T as Trait>::A: Bound`

View File

@ -177,11 +177,9 @@ crate fn placeholder_type_error(
sugg.push((arg.span, (*type_name).to_string())); sugg.push((arg.span, (*type_name).to_string()));
} else { } else {
let last = generics.iter().last().unwrap(); let last = generics.iter().last().unwrap();
sugg.push(( // Account for bounds, we want `fn foo<T: E, K>(_: K)` not `fn foo<T, K: E>(_: K)`.
// Account for bounds, we want `fn foo<T: E, K>(_: K)` not `fn foo<T, K: E>(_: K)`. let span = last.bounds_span_for_suggestions().unwrap_or(last.span.shrink_to_hi());
last.bounds_span().unwrap_or(last.span).shrink_to_hi(), sugg.push((span, format!(", {}", type_name)));
format!(", {}", type_name),
));
} }
let mut err = bad_placeholder_type(tcx, placeholder_types, kind); let mut err = bad_placeholder_type(tcx, placeholder_types, kind);

View File

@ -12,8 +12,8 @@ LL | impl<T: NotNull> IntoNullable for T {
= note: this error originates in the derive macro `Copy` (in Nightly builds, run with -Z macro-backtrace for more info) = note: this error originates in the derive macro `Copy` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider further restricting the associated type help: consider further restricting the associated type
| |
LL | Expr: Expression<SqlType=<Col::SqlType as IntoNullable>::Nullable>, <Col as Expression>::SqlType: NotNull LL | Expr: Expression<SqlType=<Col::SqlType as IntoNullable>::Nullable>, <Col as Expression>::SqlType: NotNull,
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | +++++++++++++++++++++++++++++++++++++++
error: aborting due to previous error error: aborting due to previous error