Skip to content
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

use locations with call-site hygiene for UI errors #4666

Closed
wants to merge 2 commits into from
Closed
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
3 changes: 1 addition & 2 deletions pyo3-macros-backend/src/deprecations.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use crate::method::{FnArg, FnSpec};
use proc_macro2::TokenStream;
use quote::quote_spanned;

pub(crate) fn deprecate_trailing_option_default(spec: &FnSpec<'_>) -> TokenStream {
if spec.signature.attribute.is_none()
Expand Down Expand Up @@ -42,7 +41,7 @@ pub(crate) fn deprecate_trailing_option_default(spec: &FnSpec<'_>) -> TokenStrea
deprecation_msg.push_str(
"))]` to this function to silence this warning and keep the current behavior",
);
quote_spanned! { spec.name.span() =>
quote_at_location! { spec.name.span() =>
#[deprecated(note = #deprecation_msg)]
#[allow(dead_code)]
const SIGNATURE: () = ();
Expand Down
6 changes: 3 additions & 3 deletions pyo3-macros-backend/src/intopyobject.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::attributes::{self, get_pyo3_options, CrateAttribute};
use crate::utils::Ctx;
use proc_macro2::{Span, TokenStream};
use quote::{format_ident, quote, quote_spanned};
use quote::{format_ident, quote};
use syn::ext::IdentExt;
use syn::parse::{Parse, ParseStream};
use syn::spanned::Spanned as _;
Expand Down Expand Up @@ -354,7 +354,7 @@ impl<'a> Container<'a> {
target: quote! {<#ty as #pyo3_path::conversion::IntoPyObject<'py>>::Target},
output: quote! {<#ty as #pyo3_path::conversion::IntoPyObject<'py>>::Output},
error: quote! {<#ty as #pyo3_path::conversion::IntoPyObject<'py>>::Error},
body: quote_spanned! { ty.span() =>
body: quote_at_location! { ty.span() =>
#unpack
<#ty as #pyo3_path::conversion::IntoPyObject<'py>>::into_pyobject(arg0, py)
},
Expand Down Expand Up @@ -421,7 +421,7 @@ impl<'a> Container<'a> {
.map(|(i, f)| {
let ty = &f.field.ty;
let value = Ident::new(&format!("arg{i}"), f.field.ty.span());
quote_spanned! { f.field.ty.span() =>
quote_at_location! { f.field.ty.span() =>
<#ty as #pyo3_path::conversion::IntoPyObject>::into_pyobject(#value, py)
.map(#pyo3_path::BoundObject::into_any)
.map(#pyo3_path::BoundObject::into_bound)?,
Expand Down
12 changes: 6 additions & 6 deletions pyo3-macros-backend/src/method.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::ffi::CString;
use std::fmt::Display;

use proc_macro2::{Span, TokenStream};
use quote::{format_ident, quote, quote_spanned, ToTokens};
use quote::{format_ident, quote, ToTokens};
use syn::{ext::IdentExt, spanned::Spanned, Ident, Result};

use crate::deprecations::deprecate_trailing_option_default;
Expand Down Expand Up @@ -265,7 +265,7 @@ impl FnType {
let py = syn::Ident::new("py", Span::call_site());
let slf: Ident = syn::Ident::new("_slf", Span::call_site());
let pyo3_path = pyo3_path.to_tokens_spanned(*span);
let ret = quote_spanned! { *span =>
let ret = quote_at_location! { *span =>
#[allow(clippy::useless_conversion)]
::std::convert::Into::into(
#pyo3_path::impl_::pymethods::BoundRef::ref_from_ptr(#py, &*(&#slf as *const _ as *const *mut _))
Expand All @@ -278,7 +278,7 @@ impl FnType {
let py = syn::Ident::new("py", Span::call_site());
let slf: Ident = syn::Ident::new("_slf", Span::call_site());
let pyo3_path = pyo3_path.to_tokens_spanned(*span);
let ret = quote_spanned! { *span =>
let ret = quote_at_location! { *span =>
#[allow(clippy::useless_conversion)]
::std::convert::Into::into(
#pyo3_path::impl_::pymethods::BoundRef::ref_from_ptr(#py, &*(&#slf as *const _ as *const *mut _))
Expand Down Expand Up @@ -342,7 +342,7 @@ impl SelfType {
let holder = holders.push_holder(*span);
let pyo3_path = pyo3_path.to_tokens_spanned(*span);
error_mode.handle_error(
quote_spanned! { *span =>
quote_at_location! { *span =>
#pyo3_path::impl_::extract_argument::#method::<#cls>(
#pyo3_path::impl_::pymethods::BoundRef::ref_from_ptr(#py, &#slf).0,
&mut #holder,
Expand All @@ -354,7 +354,7 @@ impl SelfType {
SelfType::TryFromBoundRef(span) => {
let pyo3_path = pyo3_path.to_tokens_spanned(*span);
error_mode.handle_error(
quote_spanned! { *span =>
quote_at_location! { *span =>
#pyo3_path::impl_::pymethods::BoundRef::ref_from_ptr(#py, &#slf).downcast::<#cls>()
.map_err(::std::convert::Into::<#pyo3_path::PyErr>::into)
.and_then(
Expand Down Expand Up @@ -824,7 +824,7 @@ impl<'a> FnSpec<'a> {
let self_arg = self
.tp
.self_arg(cls, ExtractErrorMode::Raise, &mut holders, ctx);
let call = quote_spanned! {*output_span=> #rust_name(#self_arg #(#args),*) };
let call = quote_at_location! {*output_span=> #rust_name(#self_arg #(#args),*) };
let init_holders = holders.init_holders(ctx);
quote! {
unsafe fn #ident(
Expand Down
10 changes: 5 additions & 5 deletions pyo3-macros-backend/src/params.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::{
quotes::some_wrap,
};
use proc_macro2::{Span, TokenStream};
use quote::{format_ident, quote, quote_spanned};
use quote::{format_ident, quote};
use syn::spanned::Spanned;

pub struct Holders {
Expand Down Expand Up @@ -61,7 +61,7 @@ pub fn impl_arg_params(
.filter_map(|(i, arg)| {
let from_py_with = &arg.from_py_with()?.value;
let from_py_with_holder = format_ident!("from_py_with_{}", i);
Some(quote_spanned! { from_py_with.span() =>
Some(quote_at_location! { from_py_with.span() =>
let #from_py_with_holder = #from_py_with;
})
})
Expand Down Expand Up @@ -196,7 +196,7 @@ fn impl_arg_param(
FnArg::VarArgs(arg) => {
let holder = holders.push_holder(arg.name.span());
let name_str = arg.name.to_string();
quote_spanned! { arg.name.span() =>
quote_at_location! { arg.name.span() =>
#pyo3_path::impl_::extract_argument::extract_argument(
&_args,
&mut #holder,
Expand All @@ -207,7 +207,7 @@ fn impl_arg_param(
FnArg::KwArgs(arg) => {
let holder = holders.push_holder(arg.name.span());
let name_str = arg.name.to_string();
quote_spanned! { arg.name.span() =>
quote_at_location! { arg.name.span() =>
#pyo3_path::impl_::extract_argument::extract_optional_argument(
_kwargs.as_deref(),
&mut #holder,
Expand Down Expand Up @@ -236,7 +236,7 @@ pub(crate) fn impl_regular_arg_param(
// Use this macro inside this function, to ensure that all code generated here is associated
// with the function argument
macro_rules! quote_arg_span {
($($tokens:tt)*) => { quote_spanned!(arg.ty.span() => $($tokens)*) }
($($tokens:tt)*) => { quote_at_location!(arg.ty.span() => $($tokens)*) }
}

let name_str = arg.name.to_string();
Expand Down
14 changes: 7 additions & 7 deletions pyo3-macros-backend/src/pyclass.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::borrow::Cow;
use std::fmt::Debug;

use proc_macro2::{Ident, Span, TokenStream};
use quote::{format_ident, quote, quote_spanned, ToTokens};
use quote::{format_ident, quote, ToTokens};
use syn::ext::IdentExt;
use syn::parse::{Parse, ParseStream};
use syn::punctuated::Punctuated;
Expand Down Expand Up @@ -1835,7 +1835,7 @@ fn pyclass_richcmp_arms(
.map(|eq| eq.span)
.or(options.eq_int.map(|eq_int| eq_int.span))
.map(|span| {
quote_spanned! { span =>
quote_at_location! { span =>
#pyo3_path::pyclass::CompareOp::Eq => {
::std::result::Result::Ok(#pyo3_path::conversion::IntoPy::into_py(self_val == other, py))
},
Expand All @@ -1853,7 +1853,7 @@ fn pyclass_richcmp_arms(
let ord_arms = options
.ord
.map(|ord| {
quote_spanned! { ord.span() =>
quote_at_location! { ord.span() =>
#pyo3_path::pyclass::CompareOp::Gt => {
::std::result::Result::Ok(#pyo3_path::conversion::IntoPy::into_py(self_val > other, py))
},
Expand Down Expand Up @@ -1913,7 +1913,7 @@ fn pyclass_richcmp_simple_enum(
let arms = pyclass_richcmp_arms(&options, ctx)?;

let eq = options.eq.map(|eq| {
quote_spanned! { eq.span() =>
quote_at_location! { eq.span() =>
let self_val = self;
if let ::std::result::Result::Ok(other) = #pyo3_path::types::PyAnyMethods::downcast::<Self>(other) {
let other = &*other.borrow();
Expand All @@ -1925,7 +1925,7 @@ fn pyclass_richcmp_simple_enum(
});

let eq_int = options.eq_int.map(|eq_int| {
quote_spanned! { eq_int.span() =>
quote_at_location! { eq_int.span() =>
let self_val = self.__pyo3__int__();
if let ::std::result::Result::Ok(other) = #pyo3_path::types::PyAnyMethods::extract::<#repr_type>(other).or_else(|_| {
#pyo3_path::types::PyAnyMethods::downcast::<Self>(other).map(|o| o.borrow().__pyo3__int__())
Expand Down Expand Up @@ -2282,7 +2282,7 @@ impl<'a> PyClassImplsBuilder<'a> {
};

let pyclass_base_type_impl = attr.options.subclass.map(|subclass| {
quote_spanned! { subclass.span() =>
quote_at_location! { subclass.span() =>
impl #pyo3_path::impl_::pyclass::PyClassBaseType for #cls {
type LayoutAsBase = #pyo3_path::impl_::pycell::PyClassObject<Self>;
type BaseNativeType = <Self as #pyo3_path::impl_::pyclass::PyClassImpl>::BaseNativeType;
Expand Down Expand Up @@ -2456,7 +2456,7 @@ fn generate_cfg_check(variants: &[PyClassEnumUnitVariant<'_>], cls: &syn::Ident)
}
}

quote_spanned! {
quote_at_location! {
cls.span() =>
#[cfg(all(#(#conditions),*))]
::core::compile_error!(concat!("#[pyclass] can't be used on enums without any variants - all variants of enum `", stringify!(#cls), "` have been configured out by cfg attributes"));
Expand Down
6 changes: 3 additions & 3 deletions pyo3-macros-backend/src/pymethod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use crate::{
};
use crate::{quotes, utils};
use proc_macro2::{Span, TokenStream};
use quote::{format_ident, quote, quote_spanned, ToTokens};
use quote::{format_ident, quote, ToTokens};
use syn::{ext::IdentExt, spanned::Spanned, Result};

/// Generated code for a single pymethod item.
Expand Down Expand Up @@ -662,7 +662,7 @@ pub fn impl_py_setter_def(
if let Some(from_py_with) = &value_arg.from_py_with().as_ref().map(|f| &f.value) {
let ident = syn::Ident::new("from_py_with", from_py_with.span());
(
quote_spanned! { from_py_with.span() =>
quote_at_location! { from_py_with.span() =>
let #ident = #from_py_with;
},
ident,
Expand Down Expand Up @@ -816,7 +816,7 @@ pub fn impl_py_getter_def(

// TODO: on MSRV 1.77+, we can use `::std::mem::offset_of!` here, and it should
// make it possible for the `MaybeRuntimePyMethodDef` to be a `Static` variant.
let generator = quote_spanned! { ty.span() =>
let generator = quote_at_location! { ty.span() =>
#pyo3_path::impl_::pyclass::MaybeRuntimePyMethodDef::Runtime(
|| GENERATOR.generate(#python_name, #doc)
)
Expand Down
6 changes: 3 additions & 3 deletions pyo3-macros-backend/src/quotes.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::utils::Ctx;
use proc_macro2::TokenStream;
use quote::{quote, quote_spanned};
use quote::quote;

pub(crate) fn some_wrap(obj: TokenStream, ctx: &Ctx) -> TokenStream {
let Ctx { pyo3_path, .. } = ctx;
Expand All @@ -15,7 +15,7 @@ pub(crate) fn ok_wrap(obj: TokenStream, ctx: &Ctx) -> TokenStream {
output_span,
} = ctx;
let pyo3_path = pyo3_path.to_tokens_spanned(*output_span);
quote_spanned! { *output_span => {
quote_at_location! { *output_span => {
let obj = #obj;
#pyo3_path::impl_::wrap::converter(&obj).wrap(obj).map_err(::core::convert::Into::<#pyo3_path::PyErr>::into)
}}
Expand All @@ -28,7 +28,7 @@ pub(crate) fn map_result_into_ptr(result: TokenStream, ctx: &Ctx) -> TokenStream
} = ctx;
let pyo3_path = pyo3_path.to_tokens_spanned(*output_span);
let py = syn::Ident::new("py", proc_macro2::Span::call_site());
quote_spanned! { *output_span => {
quote_at_location! { *output_span => {
let result = #result;
#pyo3_path::impl_::wrap::converter(&result).map_into_ptr(#py, result)
}}
Expand Down
12 changes: 9 additions & 3 deletions pyo3-macros-backend/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ macro_rules! ensure_spanned {
};
}

macro_rules! quote_at_location {
($span:expr => $($tokens:tt)*) => {
quote::quote_spanned!(proc_macro2::Span::call_site().located_at($span) => $($tokens)*)
};
}

/// Check if the given type `ty` is `pyo3::Python`.
pub fn is_python(ty: &syn::Type) -> bool {
match unwrap_ty_group(ty) {
Expand Down Expand Up @@ -101,7 +107,7 @@ impl quote::ToTokens for LitCStr {
} else {
let pyo3_path = &self.pyo3_path;
let lit = self.lit.to_str().unwrap();
tokens.extend(quote::quote_spanned!(self.span => #pyo3_path::ffi::c_str!(#lit)));
tokens.extend(quote_at_location!(self.span => #pyo3_path::ffi::c_str!(#lit)));
}
}
}
Expand Down Expand Up @@ -258,8 +264,8 @@ pub enum PyO3CratePath {
impl PyO3CratePath {
pub fn to_tokens_spanned(&self, span: Span) -> TokenStream {
match self {
Self::Given(path) => quote::quote_spanned! { span => #path },
Self::Default => quote::quote_spanned! { span => ::pyo3 },
Self::Given(path) => quote_at_location! { span => #path },
Self::Default => quote_at_location! { span => ::pyo3 },
}
}
}
Expand Down
11 changes: 11 additions & 0 deletions tests/ui/deprecations.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ error: use of deprecated constant `__pyfunction_pyfunction_option_2::SIGNATURE`:
= help: add `#[pyo3(signature = (_i, _any=None))]` to this function to silence this warning and keep the current behavior
--> tests/ui/deprecations.rs:15:4
|
14 | #[pyfunction]
| ------------- in this procedural macro expansion
15 | fn pyfunction_option_2(_i: u32, _any: Option<i32>) {}
| ^^^^^^^^^^^^^^^^^^^
|
Expand All @@ -11,22 +13,31 @@ note: the lint level is defined here
|
1 | #![deny(deprecated)]
| ^^^^^^^^^^
= note: this error originates in the attribute macro `pyfunction` (in Nightly builds, run with -Z macro-backtrace for more info)

error: use of deprecated constant `__pyfunction_pyfunction_option_3::SIGNATURE`: this function has implicit defaults for the trailing `Option<T>` arguments
= note: these implicit defaults are being phased out
= help: add `#[pyo3(signature = (_i, _any=None, _foo=None))]` to this function to silence this warning and keep the current behavior
--> tests/ui/deprecations.rs:18:4
|
17 | #[pyfunction]
| ------------- in this procedural macro expansion
18 | fn pyfunction_option_3(_i: u32, _any: Option<i32>, _foo: Option<String>) {}
| ^^^^^^^^^^^^^^^^^^^
|
= note: this error originates in the attribute macro `pyfunction` (in Nightly builds, run with -Z macro-backtrace for more info)

error: use of deprecated constant `__pyfunction_pyfunction_option_4::SIGNATURE`: this function has implicit defaults for the trailing `Option<T>` arguments
= note: these implicit defaults are being phased out
= help: add `#[pyo3(signature = (_i, _any=None, _foo=None))]` to this function to silence this warning and keep the current behavior
--> tests/ui/deprecations.rs:21:4
|
20 | #[pyfunction]
| ------------- in this procedural macro expansion
21 | fn pyfunction_option_4(
| ^^^^^^^^^^^^^^^^^^^
|
= note: this error originates in the attribute macro `pyfunction` (in Nightly builds, run with -Z macro-backtrace for more info)

error: use of deprecated constant `SimpleEnumWithoutEq::__pyo3__generated____richcmp__::DEPRECATION`: Implicit equality for simple enums is deprecated. Use `#[pyclass(eq, eq_int)]` to keep the current behavior.
--> tests/ui/deprecations.rs:28:1
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/forbid_unsafe.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#![forbid(unsafe_code)]
#![forbid(unsafe_code, unsafe_op_in_unsafe_fn)]

use pyo3::*;

Expand Down
6 changes: 6 additions & 0 deletions tests/ui/invalid_cancel_handle.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ note: function defined here
error[E0277]: the trait bound `CancelHandle: PyFunctionArgument<'_, '_>` is not satisfied
--> tests/ui/invalid_cancel_handle.rs:20:50
|
19 | #[pyfunction]
| ------------- in this procedural macro expansion
20 | async fn missing_cancel_handle_attribute(_param: pyo3::coroutine::CancelHandle) {}
| ^^^^ the trait `PyClass` is not implemented for `CancelHandle`, which is required by `CancelHandle: PyFunctionArgument<'_, '_>`
|
Expand All @@ -56,10 +58,13 @@ note: required by a bound in `extract_argument`
...
| T: PyFunctionArgument<'a, 'py>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `extract_argument`
= note: this error originates in the attribute macro `pyfunction` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0277]: the trait bound `CancelHandle: PyFunctionArgument<'_, '_>` is not satisfied
--> tests/ui/invalid_cancel_handle.rs:20:50
|
19 | #[pyfunction]
| ------------- in this procedural macro expansion
20 | async fn missing_cancel_handle_attribute(_param: pyo3::coroutine::CancelHandle) {}
| ^^^^ the trait `Clone` is not implemented for `CancelHandle`, which is required by `CancelHandle: PyFunctionArgument<'_, '_>`
|
Expand All @@ -79,3 +84,4 @@ note: required by a bound in `extract_argument`
...
| T: PyFunctionArgument<'a, 'py>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `extract_argument`
= note: this error originates in the attribute macro `pyfunction` (in Nightly builds, run with -Z macro-backtrace for more info)
4 changes: 4 additions & 0 deletions tests/ui/invalid_frozen_pyclass_borrow.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ error: cannot use `#[pyo3(set)]` on a `frozen` class
error[E0271]: type mismatch resolving `<Foo as PyClass>::Frozen == False`
--> tests/ui/invalid_frozen_pyclass_borrow.rs:11:19
|
9 | #[pymethods]
| ------------ in this procedural macro expansion
10 | impl Foo {
11 | fn mut_method(&mut self) {}
| ^ expected `False`, found `True`
|
Expand All @@ -15,6 +18,7 @@ note: required by a bound in `extract_pyclass_ref_mut`
|
| pub fn extract_pyclass_ref_mut<'a, 'py: 'a, T: PyClass<Frozen = False>>(
| ^^^^^^^^^^^^^^ required by this bound in `extract_pyclass_ref_mut`
= note: this error originates in the attribute macro `pymethods` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0271]: type mismatch resolving `<Foo as PyClass>::Frozen == False`
--> tests/ui/invalid_frozen_pyclass_borrow.rs:9:1
Expand Down
4 changes: 4 additions & 0 deletions tests/ui/invalid_property_args.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ error: `name` is useless without `get` or `set`
error[E0277]: `PhantomData<i32>` cannot be converted to a Python object
--> tests/ui/invalid_property_args.rs:45:12
|
42 | #[pyclass]
| ---------- in this procedural macro expansion
...
45 | value: ::std::marker::PhantomData<i32>,
| ^ required by `#[pyo3(get)]` to create a readable property from a field of type `PhantomData<i32>`
|
Expand All @@ -73,3 +76,4 @@ note: required by a bound in `PyClassGetterGenerator::<ClassT, FieldT, Offset, f
...
| for<'py> FieldT: PyO3GetField<'py>,
| ^^^^^^^^^^^^^^^^^ required by this bound in `PyClassGetterGenerator::<ClassT, FieldT, Offset, false, false, false, false, false>::generate`
= note: this error originates in the attribute macro `pyclass` (in Nightly builds, run with -Z macro-backtrace for more info)
Loading
Loading