Skip to content

Commit

Permalink
derive(SmartPointer): assume pointee from the single generic and bett…
Browse files Browse the repository at this point in the history
…er error messages
  • Loading branch information
dingxiangfei2009 committed Aug 23, 2024
1 parent c8b14ba commit bb43160
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 38 deletions.
76 changes: 54 additions & 22 deletions compiler/rustc_builtin_macros/src/deriving/smart_ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,20 @@ use rustc_data_structures::flat_map_in_place::FlatMapInPlace;
use rustc_expand::base::{Annotatable, ExtCtxt};
use rustc_span::symbol::{sym, Ident};
use rustc_span::{Span, Symbol};
use smallvec::{smallvec, SmallVec};
use thin_vec::{thin_vec, ThinVec};

macro_rules! path {
($span:expr, $($part:ident)::*) => { vec![$(Ident::new(sym::$part, $span),)*] }
}

enum PointeeChoice {
None,
Exactly(usize, Span),
Assumed(usize),
Ambiguous,
MultiplePointeeChoice(Span, Span),
}

pub fn expand_deriving_smart_ptr(
cx: &ExtCtxt<'_>,
span: Span,
Expand Down Expand Up @@ -68,8 +75,7 @@ pub fn expand_deriving_smart_ptr(
};

// Convert generic parameters (from the struct) into generic args.
let mut pointee_param = None;
let mut multiple_pointee_diag: SmallVec<[_; 2]> = smallvec![];
let mut pointee_param = PointeeChoice::None;
let self_params = generics
.params
.iter()
Expand All @@ -78,33 +84,59 @@ pub fn expand_deriving_smart_ptr(
GenericParamKind::Lifetime => GenericArg::Lifetime(cx.lifetime(p.span(), p.ident)),
GenericParamKind::Type { .. } => {
if p.attrs().iter().any(|attr| attr.has_name(sym::pointee)) {
if pointee_param.is_some() {
multiple_pointee_diag.push(cx.dcx().struct_span_err(
p.span(),
"`SmartPointer` can only admit one type as pointee",
));
} else {
pointee_param = Some(idx);
match pointee_param {
PointeeChoice::Assumed(_)
| PointeeChoice::Ambiguous
| PointeeChoice::None => {
pointee_param = PointeeChoice::Exactly(idx, p.span())
}
PointeeChoice::Exactly(_, another) => {
pointee_param = PointeeChoice::MultiplePointeeChoice(another, p.span())
}
PointeeChoice::MultiplePointeeChoice(_, _) => {}
}
} else {
match pointee_param {
PointeeChoice::None => pointee_param = PointeeChoice::Assumed(idx),
PointeeChoice::Assumed(_) | PointeeChoice::Ambiguous => {
pointee_param = PointeeChoice::Ambiguous
}
PointeeChoice::Exactly(_, _)
| PointeeChoice::MultiplePointeeChoice(_, _) => {}
}
}
GenericArg::Type(cx.ty_ident(p.span(), p.ident))
}
GenericParamKind::Const { .. } => GenericArg::Const(cx.const_ident(p.span(), p.ident)),
})
.collect::<Vec<_>>();
let Some(pointee_param_idx) = pointee_param else {
cx.dcx().struct_span_err(
span,
"At least one generic type should be designated as `#[pointee]` in order to derive `SmartPointer` traits",
).emit();
return;
};
if !multiple_pointee_diag.is_empty() {
for diag in multiple_pointee_diag {
diag.emit();
let pointee_param_idx = match pointee_param {
PointeeChoice::Assumed(idx) | PointeeChoice::Exactly(idx, _) => idx,
PointeeChoice::None => {
cx.dcx().struct_span_err(
span,
"`SmartPointer` can only be derived on `struct`s that are generic over at least one type",
).emit();
return;
}
return;
}
PointeeChoice::Ambiguous => {
cx.dcx().struct_span_err(
span,
"Exactly one generic parameters when there are at least two generic type parameters \
should be designated as `#[pointee]` in order to derive `SmartPointer` traits",
).emit();
return;
}
PointeeChoice::MultiplePointeeChoice(one, another) => {
cx.dcx()
.struct_span_err(
vec![one, another],
"`SmartPointer` can only admit one type as pointee",
)
.emit();
return;
}
};

// Create the type of `self`.
let path = cx.path_all(span, false, vec![name_ident], self_params.clone());
Expand Down
6 changes: 6 additions & 0 deletions tests/ui/deriving/deriving-smart-pointer-expanded.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,9 @@ where
data: &'a mut T,
x: core::marker::PhantomData<X>,
}

#[derive(SmartPointer)]
#[repr(transparent)]
struct MyPointerWithoutPointee<'a, T: ?Sized> {
ptr: &'a T,
}
15 changes: 15 additions & 0 deletions tests/ui/deriving/deriving-smart-pointer-expanded.stdout
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,18 @@ impl<'a, Y, Z: MyTrait<T> + MyTrait<__S>, T: ?Sized + MyTrait<T> +
MyTrait<__S>> ::core::ops::CoerceUnsized<MyPointer2<'a, Y, Z, __S, X>> for
MyPointer2<'a, Y, Z, T, X> where Y: MyTrait<T>, Y: MyTrait<__S> {
}

#[repr(transparent)]
struct MyPointerWithoutPointee<'a, T: ?Sized> {
ptr: &'a T,
}
#[automatically_derived]
impl<'a, T: ?Sized + ::core::marker::Unsize<__S>, __S: ?Sized>
::core::ops::DispatchFromDyn<MyPointerWithoutPointee<'a, __S>> for
MyPointerWithoutPointee<'a, T> {
}
#[automatically_derived]
impl<'a, T: ?Sized + ::core::marker::Unsize<__S>, __S: ?Sized>
::core::ops::CoerceUnsized<MyPointerWithoutPointee<'a, __S>> for
MyPointerWithoutPointee<'a, T> {
}
17 changes: 10 additions & 7 deletions tests/ui/deriving/deriving-smart-pointer-neg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,6 @@ enum NotStruct<'a, T: ?Sized> {
Variant(&'a T),
}

#[derive(SmartPointer)]
//~^ ERROR: At least one generic type should be designated as `#[pointee]` in order to derive `SmartPointer` traits
#[repr(transparent)]
struct NoPointee<'a, T: ?Sized> {
ptr: &'a T,
}

#[derive(SmartPointer)]
//~^ ERROR: `SmartPointer` can only be derived on `struct`s with at least one field
#[repr(transparent)]
Expand All @@ -30,6 +23,16 @@ struct NoFieldUnit<'a, #[pointee] T: ?Sized>();
//~^ ERROR: lifetime parameter `'a` is never used
//~| ERROR: type parameter `T` is never used

#[derive(SmartPointer)]
//~^ ERROR: `SmartPointer` can only be derived on `struct`s that are generic over at least one type
#[repr(transparent)]
struct NoGeneric<'a>(&'a u8);

#[derive(SmartPointer)]
#[repr(transparent)]
struct TooManyPointees<'a, #[pointee] A: ?Sized, #[pointee] B: ?Sized>((&'a A, &'a B));
//~^ ERROR: `SmartPointer` can only admit one type as pointee

#[derive(SmartPointer)]
//~^ ERROR: `SmartPointer` can only be derived on `struct`s with `#[repr(transparent)]`
struct NotTransparent<'a, #[pointee] T: ?Sized> {
Expand Down
24 changes: 15 additions & 9 deletions tests/ui/deriving/deriving-smart-pointer-neg.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ LL | #[derive(SmartPointer)]
|
= note: this error originates in the derive macro `SmartPointer` (in Nightly builds, run with -Z macro-backtrace for more info)

error: At least one generic type should be designated as `#[pointee]` in order to derive `SmartPointer` traits
error: `SmartPointer` can only be derived on `struct`s with at least one field
--> $DIR/deriving-smart-pointer-neg.rs:12:10
|
LL | #[derive(SmartPointer)]
Expand All @@ -22,60 +22,66 @@ LL | #[derive(SmartPointer)]
|
= note: this error originates in the derive macro `SmartPointer` (in Nightly builds, run with -Z macro-backtrace for more info)

error: `SmartPointer` can only be derived on `struct`s with at least one field
error: `SmartPointer` can only be derived on `struct`s that are generic over at least one type
--> $DIR/deriving-smart-pointer-neg.rs:26:10
|
LL | #[derive(SmartPointer)]
| ^^^^^^^^^^^^
|
= note: this error originates in the derive macro `SmartPointer` (in Nightly builds, run with -Z macro-backtrace for more info)

error: `SmartPointer` can only admit one type as pointee
--> $DIR/deriving-smart-pointer-neg.rs:33:39
|
LL | struct TooManyPointees<'a, #[pointee] A: ?Sized, #[pointee] B: ?Sized>((&'a A, &'a B));
| ^ ^

error: `SmartPointer` can only be derived on `struct`s with `#[repr(transparent)]`
--> $DIR/deriving-smart-pointer-neg.rs:33:10
--> $DIR/deriving-smart-pointer-neg.rs:36:10
|
LL | #[derive(SmartPointer)]
| ^^^^^^^^^^^^
|
= note: this error originates in the derive macro `SmartPointer` (in Nightly builds, run with -Z macro-backtrace for more info)

error: `derive(SmartPointer)` requires T to be marked `?Sized`
--> $DIR/deriving-smart-pointer-neg.rs:41:36
--> $DIR/deriving-smart-pointer-neg.rs:44:36
|
LL | struct NoMaybeSized<'a, #[pointee] T> {
| ^

error[E0392]: lifetime parameter `'a` is never used
--> $DIR/deriving-smart-pointer-neg.rs:22:16
--> $DIR/deriving-smart-pointer-neg.rs:15:16
|
LL | struct NoField<'a, #[pointee] T: ?Sized> {}
| ^^ unused lifetime parameter
|
= help: consider removing `'a`, referring to it in a field, or using a marker such as `PhantomData`

error[E0392]: type parameter `T` is never used
--> $DIR/deriving-smart-pointer-neg.rs:22:31
--> $DIR/deriving-smart-pointer-neg.rs:15:31
|
LL | struct NoField<'a, #[pointee] T: ?Sized> {}
| ^ unused type parameter
|
= help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData`

error[E0392]: lifetime parameter `'a` is never used
--> $DIR/deriving-smart-pointer-neg.rs:29:20
--> $DIR/deriving-smart-pointer-neg.rs:22:20
|
LL | struct NoFieldUnit<'a, #[pointee] T: ?Sized>();
| ^^ unused lifetime parameter
|
= help: consider removing `'a`, referring to it in a field, or using a marker such as `PhantomData`

error[E0392]: type parameter `T` is never used
--> $DIR/deriving-smart-pointer-neg.rs:29:35
--> $DIR/deriving-smart-pointer-neg.rs:22:35
|
LL | struct NoFieldUnit<'a, #[pointee] T: ?Sized>();
| ^ unused type parameter
|
= help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData`

error: aborting due to 10 previous errors
error: aborting due to 11 previous errors

For more information about this error, try `rustc --explain E0392`.

0 comments on commit bb43160

Please sign in to comment.