Merge pull request #3311 from davidhewitt/frozen-receiver-error
improve error span for mutable access to `#[pyclass(frozen)]`
This commit is contained in:
commit
bb05896324
|
@ -8,7 +8,7 @@ use quote::ToTokens;
|
||||||
use quote::{quote, quote_spanned};
|
use quote::{quote, quote_spanned};
|
||||||
use syn::ext::IdentExt;
|
use syn::ext::IdentExt;
|
||||||
use syn::spanned::Spanned;
|
use syn::spanned::Spanned;
|
||||||
use syn::Result;
|
use syn::{Result, Token};
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct FnArg<'a> {
|
pub struct FnArg<'a> {
|
||||||
|
@ -136,7 +136,7 @@ impl FnType {
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum SelfType {
|
pub enum SelfType {
|
||||||
Receiver { mutable: bool },
|
Receiver { mutable: bool, span: Span },
|
||||||
TryFromPyCell(Span),
|
TryFromPyCell(Span),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,38 +146,55 @@ pub enum ExtractErrorMode {
|
||||||
Raise,
|
Raise,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ExtractErrorMode {
|
||||||
|
pub fn handle_error(self, py: &syn::Ident, extract: TokenStream) -> TokenStream {
|
||||||
|
match self {
|
||||||
|
ExtractErrorMode::Raise => quote! { #extract? },
|
||||||
|
ExtractErrorMode::NotImplemented => quote! {
|
||||||
|
match #extract {
|
||||||
|
::std::result::Result::Ok(value) => value,
|
||||||
|
::std::result::Result::Err(_) => { return _pyo3::callback::convert(#py, #py.NotImplemented()); },
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl SelfType {
|
impl SelfType {
|
||||||
pub fn receiver(&self, cls: &syn::Type, error_mode: ExtractErrorMode) -> TokenStream {
|
pub fn receiver(&self, cls: &syn::Type, error_mode: ExtractErrorMode) -> TokenStream {
|
||||||
let cell = match error_mode {
|
let py = syn::Ident::new("_py", Span::call_site());
|
||||||
ExtractErrorMode::Raise => {
|
let _slf = syn::Ident::new("_slf", Span::call_site());
|
||||||
quote! { _py.from_borrowed_ptr::<_pyo3::PyAny>(_slf).downcast::<_pyo3::PyCell<#cls>>()? }
|
|
||||||
}
|
|
||||||
ExtractErrorMode::NotImplemented => {
|
|
||||||
quote! {
|
|
||||||
match _py.from_borrowed_ptr::<_pyo3::PyAny>(_slf).downcast::<_pyo3::PyCell<#cls>>() {
|
|
||||||
::std::result::Result::Ok(cell) => cell,
|
|
||||||
::std::result::Result::Err(_) => return _pyo3::callback::convert(_py, _py.NotImplemented()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
match self {
|
match self {
|
||||||
SelfType::Receiver { mutable: false } => {
|
SelfType::Receiver { span, mutable } => {
|
||||||
quote! {
|
let (method, mutability) = if *mutable {
|
||||||
let _cell = #cell;
|
(
|
||||||
let _ref = _cell.try_borrow()?;
|
quote_spanned! { *span => extract_pyclass_ref_mut },
|
||||||
let _slf: &#cls = &*_ref;
|
Some(Token![mut](*span)),
|
||||||
}
|
)
|
||||||
}
|
} else {
|
||||||
SelfType::Receiver { mutable: true } => {
|
(quote_spanned! { *span => extract_pyclass_ref }, None)
|
||||||
quote! {
|
};
|
||||||
let _cell = #cell;
|
let extract = error_mode.handle_error(
|
||||||
let mut _ref = _cell.try_borrow_mut()?;
|
&py,
|
||||||
let _slf: &mut #cls = &mut *_ref;
|
quote_spanned! { *span =>
|
||||||
|
_pyo3::impl_::extract_argument::#method(
|
||||||
|
#py.from_borrowed_ptr::<_pyo3::PyAny>(#_slf),
|
||||||
|
&mut holder,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
);
|
||||||
|
quote_spanned! { *span =>
|
||||||
|
let mut holder = _pyo3::impl_::extract_argument::FunctionArgumentHolder::INIT;
|
||||||
|
let #_slf: &#mutability #cls = #extract;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SelfType::TryFromPyCell(span) => {
|
SelfType::TryFromPyCell(span) => {
|
||||||
let _slf = quote! { _slf };
|
let cell = error_mode.handle_error(
|
||||||
|
&py,
|
||||||
|
quote!{
|
||||||
|
_py.from_borrowed_ptr::<_pyo3::PyAny>(_slf).downcast::<_pyo3::PyCell<#cls>>()
|
||||||
|
}
|
||||||
|
);
|
||||||
quote_spanned! { *span =>
|
quote_spanned! { *span =>
|
||||||
let _cell = #cell;
|
let _cell = #cell;
|
||||||
#[allow(clippy::useless_conversion)] // In case _slf is PyCell<Self>
|
#[allow(clippy::useless_conversion)] // In case _slf is PyCell<Self>
|
||||||
|
@ -247,8 +264,9 @@ pub fn parse_method_receiver(arg: &syn::FnArg) -> Result<SelfType> {
|
||||||
) => {
|
) => {
|
||||||
bail_spanned!(recv.span() => RECEIVER_BY_VALUE_ERR);
|
bail_spanned!(recv.span() => RECEIVER_BY_VALUE_ERR);
|
||||||
}
|
}
|
||||||
syn::FnArg::Receiver(syn::Receiver { mutability, .. }) => Ok(SelfType::Receiver {
|
syn::FnArg::Receiver(recv @ syn::Receiver { mutability, .. }) => Ok(SelfType::Receiver {
|
||||||
mutable: mutability.is_some(),
|
mutable: mutability.is_some(),
|
||||||
|
span: recv.span(),
|
||||||
}),
|
}),
|
||||||
syn::FnArg::Typed(syn::PatType { ty, .. }) => {
|
syn::FnArg::Typed(syn::PatType { ty, .. }) => {
|
||||||
if let syn::Type::ImplTrait(_) = &**ty {
|
if let syn::Type::ImplTrait(_) = &**ty {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
|
use crate::attributes::kw::frozen;
|
||||||
use crate::attributes::{
|
use crate::attributes::{
|
||||||
self, kw, take_pyo3_options, CrateAttribute, ExtendsAttribute, FreelistAttribute,
|
self, kw, take_pyo3_options, CrateAttribute, ExtendsAttribute, FreelistAttribute,
|
||||||
ModuleAttribute, NameAttribute, NameLitStr, TextSignatureAttribute,
|
ModuleAttribute, NameAttribute, NameLitStr, TextSignatureAttribute,
|
||||||
|
@ -355,7 +356,7 @@ fn impl_class(
|
||||||
cls,
|
cls,
|
||||||
args,
|
args,
|
||||||
methods_type,
|
methods_type,
|
||||||
descriptors_to_items(cls, field_options)?,
|
descriptors_to_items(cls, args.options.frozen, field_options)?,
|
||||||
vec![],
|
vec![],
|
||||||
)
|
)
|
||||||
.doc(doc)
|
.doc(doc)
|
||||||
|
@ -674,6 +675,7 @@ fn extract_variant_data(variant: &mut syn::Variant) -> syn::Result<PyClassEnumVa
|
||||||
|
|
||||||
fn descriptors_to_items(
|
fn descriptors_to_items(
|
||||||
cls: &syn::Ident,
|
cls: &syn::Ident,
|
||||||
|
frozen: Option<frozen>,
|
||||||
field_options: Vec<(&syn::Field, FieldPyO3Options)>,
|
field_options: Vec<(&syn::Field, FieldPyO3Options)>,
|
||||||
) -> syn::Result<Vec<MethodAndMethodDef>> {
|
) -> syn::Result<Vec<MethodAndMethodDef>> {
|
||||||
let ty = syn::parse_quote!(#cls);
|
let ty = syn::parse_quote!(#cls);
|
||||||
|
@ -700,7 +702,8 @@ fn descriptors_to_items(
|
||||||
items.push(getter);
|
items.push(getter);
|
||||||
}
|
}
|
||||||
|
|
||||||
if options.set.is_some() {
|
if let Some(set) = options.set {
|
||||||
|
ensure_spanned!(frozen.is_none(), set.span() => "cannot use `#[pyo3(set)]` on a `frozen` class");
|
||||||
let setter = impl_py_setter_def(
|
let setter = impl_py_setter_def(
|
||||||
&ty,
|
&ty,
|
||||||
PropertyType::Descriptor {
|
PropertyType::Descriptor {
|
||||||
|
|
|
@ -523,9 +523,11 @@ pub fn impl_py_setter_def(
|
||||||
};
|
};
|
||||||
|
|
||||||
let slf = match property_type {
|
let slf = match property_type {
|
||||||
PropertyType::Descriptor { .. } => {
|
PropertyType::Descriptor { .. } => SelfType::Receiver {
|
||||||
SelfType::Receiver { mutable: true }.receiver(cls, ExtractErrorMode::Raise)
|
mutable: true,
|
||||||
|
span: Span::call_site(),
|
||||||
}
|
}
|
||||||
|
.receiver(cls, ExtractErrorMode::Raise),
|
||||||
PropertyType::Function { self_type, .. } => {
|
PropertyType::Function { self_type, .. } => {
|
||||||
self_type.receiver(cls, ExtractErrorMode::Raise)
|
self_type.receiver(cls, ExtractErrorMode::Raise)
|
||||||
}
|
}
|
||||||
|
@ -638,9 +640,11 @@ pub fn impl_py_getter_def(
|
||||||
};
|
};
|
||||||
|
|
||||||
let slf = match property_type {
|
let slf = match property_type {
|
||||||
PropertyType::Descriptor { .. } => {
|
PropertyType::Descriptor { .. } => SelfType::Receiver {
|
||||||
SelfType::Receiver { mutable: false }.receiver(cls, ExtractErrorMode::Raise)
|
mutable: false,
|
||||||
|
span: Span::call_site(),
|
||||||
}
|
}
|
||||||
|
.receiver(cls, ExtractErrorMode::Raise),
|
||||||
PropertyType::Function { self_type, .. } => {
|
PropertyType::Function { self_type, .. } => {
|
||||||
self_type.receiver(cls, ExtractErrorMode::Raise)
|
self_type.receiver(cls, ExtractErrorMode::Raise)
|
||||||
}
|
}
|
||||||
|
@ -949,8 +953,7 @@ impl Ty {
|
||||||
#ident.to_borrowed_any(#py)
|
#ident.to_borrowed_any(#py)
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
Ty::CompareOp => handle_error(
|
Ty::CompareOp => extract_error_mode.handle_error(
|
||||||
extract_error_mode,
|
|
||||||
py,
|
py,
|
||||||
quote! {
|
quote! {
|
||||||
_pyo3::class::basic::CompareOp::from_raw(#ident)
|
_pyo3::class::basic::CompareOp::from_raw(#ident)
|
||||||
|
@ -959,8 +962,7 @@ impl Ty {
|
||||||
),
|
),
|
||||||
Ty::PySsizeT => {
|
Ty::PySsizeT => {
|
||||||
let ty = arg.ty;
|
let ty = arg.ty;
|
||||||
handle_error(
|
extract_error_mode.handle_error(
|
||||||
extract_error_mode,
|
|
||||||
py,
|
py,
|
||||||
quote! {
|
quote! {
|
||||||
::std::convert::TryInto::<#ty>::try_into(#ident).map_err(|e| _pyo3::exceptions::PyValueError::new_err(e.to_string()))
|
::std::convert::TryInto::<#ty>::try_into(#ident).map_err(|e| _pyo3::exceptions::PyValueError::new_err(e.to_string()))
|
||||||
|
@ -973,30 +975,13 @@ impl Ty {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_error(
|
|
||||||
extract_error_mode: ExtractErrorMode,
|
|
||||||
py: &syn::Ident,
|
|
||||||
extract: TokenStream,
|
|
||||||
) -> TokenStream {
|
|
||||||
match extract_error_mode {
|
|
||||||
ExtractErrorMode::Raise => quote! { #extract? },
|
|
||||||
ExtractErrorMode::NotImplemented => quote! {
|
|
||||||
match #extract {
|
|
||||||
::std::result::Result::Ok(value) => value,
|
|
||||||
::std::result::Result::Err(_) => { return _pyo3::callback::convert(#py, #py.NotImplemented()); },
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn extract_object(
|
fn extract_object(
|
||||||
extract_error_mode: ExtractErrorMode,
|
extract_error_mode: ExtractErrorMode,
|
||||||
py: &syn::Ident,
|
py: &syn::Ident,
|
||||||
name: &str,
|
name: &str,
|
||||||
source: TokenStream,
|
source: TokenStream,
|
||||||
) -> TokenStream {
|
) -> TokenStream {
|
||||||
handle_error(
|
extract_error_mode.handle_error(
|
||||||
extract_error_mode,
|
|
||||||
py,
|
py,
|
||||||
quote! {
|
quote! {
|
||||||
_pyo3::impl_::extract_argument::extract_argument(
|
_pyo3::impl_::extract_argument::extract_argument(
|
||||||
|
|
|
@ -6,6 +6,11 @@ pub struct Foo {
|
||||||
field: u32,
|
field: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[pymethods]
|
||||||
|
impl Foo {
|
||||||
|
fn mut_method(&mut self) {}
|
||||||
|
}
|
||||||
|
|
||||||
fn borrow_mut_fails(foo: Py<Foo>, py: Python) {
|
fn borrow_mut_fails(foo: Py<Foo>, py: Python) {
|
||||||
let borrow = foo.as_ref(py).borrow_mut();
|
let borrow = foo.as_ref(py).borrow_mut();
|
||||||
}
|
}
|
||||||
|
@ -28,4 +33,10 @@ fn pyclass_get_of_mutable_class_fails(class: &PyCell<MutableBase>) {
|
||||||
class.get();
|
class.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[pyclass(frozen)]
|
||||||
|
pub struct SetOnFrozenClass {
|
||||||
|
#[pyo3(set)]
|
||||||
|
field: u32,
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
|
|
@ -1,7 +1,25 @@
|
||||||
error[E0271]: type mismatch resolving `<Foo as PyClass>::Frozen == False`
|
error: cannot use `#[pyo3(set)]` on a `frozen` class
|
||||||
--> tests/ui/invalid_frozen_pyclass_borrow.rs:10:33
|
--> tests/ui/invalid_frozen_pyclass_borrow.rs:38:12
|
||||||
|
|
|
|
||||||
10 | let borrow = foo.as_ref(py).borrow_mut();
|
38 | #[pyo3(set)]
|
||||||
|
| ^^^
|
||||||
|
|
||||||
|
error[E0271]: type mismatch resolving `<Foo as PyClass>::Frozen == False`
|
||||||
|
--> tests/ui/invalid_frozen_pyclass_borrow.rs:11:19
|
||||||
|
|
|
||||||
|
11 | fn mut_method(&mut self) {}
|
||||||
|
| ^ expected `False`, found `True`
|
||||||
|
|
|
||||||
|
note: required by a bound in `extract_pyclass_ref_mut`
|
||||||
|
--> src/impl_/extract_argument.rs
|
||||||
|
|
|
||||||
|
| pub fn extract_pyclass_ref_mut<'a, 'py: 'a, T: PyClass<Frozen = False>>(
|
||||||
|
| ^^^^^^^^^^^^^^ required by this bound in `extract_pyclass_ref_mut`
|
||||||
|
|
||||||
|
error[E0271]: type mismatch resolving `<Foo as PyClass>::Frozen == False`
|
||||||
|
--> tests/ui/invalid_frozen_pyclass_borrow.rs:15:33
|
||||||
|
|
|
||||||
|
15 | let borrow = foo.as_ref(py).borrow_mut();
|
||||||
| ^^^^^^^^^^ expected `False`, found `True`
|
| ^^^^^^^^^^ expected `False`, found `True`
|
||||||
|
|
|
|
||||||
note: required by a bound in `pyo3::PyCell::<T>::borrow_mut`
|
note: required by a bound in `pyo3::PyCell::<T>::borrow_mut`
|
||||||
|
@ -11,9 +29,9 @@ note: required by a bound in `pyo3::PyCell::<T>::borrow_mut`
|
||||||
| ^^^^^^^^^^^^^^ required by this bound in `PyCell::<T>::borrow_mut`
|
| ^^^^^^^^^^^^^^ required by this bound in `PyCell::<T>::borrow_mut`
|
||||||
|
|
||||||
error[E0271]: type mismatch resolving `<ImmutableChild as PyClass>::Frozen == False`
|
error[E0271]: type mismatch resolving `<ImmutableChild as PyClass>::Frozen == False`
|
||||||
--> tests/ui/invalid_frozen_pyclass_borrow.rs:20:35
|
--> tests/ui/invalid_frozen_pyclass_borrow.rs:25:35
|
||||||
|
|
|
|
||||||
20 | let borrow = child.as_ref(py).borrow_mut();
|
25 | let borrow = child.as_ref(py).borrow_mut();
|
||||||
| ^^^^^^^^^^ expected `False`, found `True`
|
| ^^^^^^^^^^ expected `False`, found `True`
|
||||||
|
|
|
|
||||||
note: required by a bound in `pyo3::PyCell::<T>::borrow_mut`
|
note: required by a bound in `pyo3::PyCell::<T>::borrow_mut`
|
||||||
|
@ -23,9 +41,9 @@ note: required by a bound in `pyo3::PyCell::<T>::borrow_mut`
|
||||||
| ^^^^^^^^^^^^^^ required by this bound in `PyCell::<T>::borrow_mut`
|
| ^^^^^^^^^^^^^^ required by this bound in `PyCell::<T>::borrow_mut`
|
||||||
|
|
||||||
error[E0271]: type mismatch resolving `<MutableBase as PyClass>::Frozen == True`
|
error[E0271]: type mismatch resolving `<MutableBase as PyClass>::Frozen == True`
|
||||||
--> tests/ui/invalid_frozen_pyclass_borrow.rs:24:11
|
--> tests/ui/invalid_frozen_pyclass_borrow.rs:29:11
|
||||||
|
|
|
|
||||||
24 | class.get();
|
29 | class.get();
|
||||||
| ^^^ expected `True`, found `False`
|
| ^^^ expected `True`, found `False`
|
||||||
|
|
|
|
||||||
note: required by a bound in `pyo3::Py::<T>::get`
|
note: required by a bound in `pyo3::Py::<T>::get`
|
||||||
|
@ -35,9 +53,9 @@ note: required by a bound in `pyo3::Py::<T>::get`
|
||||||
| ^^^^^^^^^^^^^ required by this bound in `Py::<T>::get`
|
| ^^^^^^^^^^^^^ required by this bound in `Py::<T>::get`
|
||||||
|
|
||||||
error[E0271]: type mismatch resolving `<MutableBase as PyClass>::Frozen == True`
|
error[E0271]: type mismatch resolving `<MutableBase as PyClass>::Frozen == True`
|
||||||
--> tests/ui/invalid_frozen_pyclass_borrow.rs:28:11
|
--> tests/ui/invalid_frozen_pyclass_borrow.rs:33:11
|
||||||
|
|
|
|
||||||
28 | class.get();
|
33 | class.get();
|
||||||
| ^^^ expected `True`, found `False`
|
| ^^^ expected `True`, found `False`
|
||||||
|
|
|
|
||||||
note: required by a bound in `pyo3::PyCell::<T>::get`
|
note: required by a bound in `pyo3::PyCell::<T>::get`
|
||||||
|
|
Loading…
Reference in a new issue