forked from Rust-related/RustPython
Merge pull request #1688 from RustPython/coolreader18/pyimpl-trait
Allow #[pyimpl] on traits
This commit is contained in:
7
Cargo.lock
generated
7
Cargo.lock
generated
@@ -1312,11 +1312,11 @@ version = "0.1.1"
|
||||
dependencies = [
|
||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"maplit 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustpython-bytecode 0.1.1",
|
||||
"rustpython-compiler 0.1.1",
|
||||
"syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1328,7 +1328,6 @@ dependencies = [
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-bigint 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unic-emoji-char 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unic-ucd-ident 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wtf8 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
||||
@@ -11,9 +11,9 @@ edition = "2018"
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
syn = { version = "0.15.29", features = ["full"] }
|
||||
quote = "0.6.11"
|
||||
proc-macro2 = "0.4.27"
|
||||
syn = { version = "1.0", features = ["full"] }
|
||||
quote = "1.0"
|
||||
proc-macro2 = "1.0"
|
||||
rustpython-compiler = { path = "../compiler", version = "0.1.1" }
|
||||
rustpython-bytecode = { path = "../bytecode", version = "0.1.1" }
|
||||
maplit = "1.0"
|
||||
|
||||
@@ -176,7 +176,11 @@ impl PyCompileInput {
|
||||
|
||||
for meta in &self.metas {
|
||||
if let Meta::NameValue(name_value) = meta {
|
||||
if name_value.ident == "mode" {
|
||||
let ident = match name_value.path.get_ident() {
|
||||
Some(ident) => ident,
|
||||
None => continue,
|
||||
};
|
||||
if ident == "mode" {
|
||||
match &name_value.lit {
|
||||
Lit::Str(s) => match s.value().parse() {
|
||||
Ok(mode_val) => mode = Some(mode_val),
|
||||
@@ -184,12 +188,12 @@ impl PyCompileInput {
|
||||
},
|
||||
_ => bail_span!(name_value.lit, "mode must be a string"),
|
||||
}
|
||||
} else if name_value.ident == "module_name" {
|
||||
} else if ident == "module_name" {
|
||||
module_name = Some(match &name_value.lit {
|
||||
Lit::Str(s) => s.value(),
|
||||
_ => bail_span!(name_value.lit, "module_name must be string"),
|
||||
})
|
||||
} else if name_value.ident == "source" {
|
||||
} else if ident == "source" {
|
||||
assert_source_empty(&source)?;
|
||||
let code = match &name_value.lit {
|
||||
Lit::Str(s) => s.value(),
|
||||
@@ -199,7 +203,7 @@ impl PyCompileInput {
|
||||
kind: CompilationSourceKind::SourceCode(code),
|
||||
span: extract_spans(&name_value).unwrap(),
|
||||
});
|
||||
} else if name_value.ident == "file" {
|
||||
} else if ident == "file" {
|
||||
assert_source_empty(&source)?;
|
||||
let path = match &name_value.lit {
|
||||
Lit::Str(s) => PathBuf::from(s.value()),
|
||||
@@ -209,7 +213,7 @@ impl PyCompileInput {
|
||||
kind: CompilationSourceKind::File(path),
|
||||
span: extract_spans(&name_value).unwrap(),
|
||||
});
|
||||
} else if name_value.ident == "dir" {
|
||||
} else if ident == "dir" {
|
||||
assert_source_empty(&source)?;
|
||||
let path = match &name_value.lit {
|
||||
Lit::Str(s) => PathBuf::from(s.value()),
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::util::path_eq;
|
||||
use crate::Diagnostic;
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::quote;
|
||||
@@ -45,7 +46,9 @@ impl ArgAttribute {
|
||||
err_span!(list, "There must be at least one argument to #[pyarg()]")
|
||||
})?;
|
||||
let kind = match first_arg {
|
||||
NestedMeta::Meta(Meta::Word(ident)) => ParameterKind::from_ident(ident),
|
||||
NestedMeta::Meta(Meta::Path(path)) => {
|
||||
path.get_ident().and_then(ParameterKind::from_ident)
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
let kind = kind.ok_or_else(|| {
|
||||
@@ -79,21 +82,21 @@ impl ArgAttribute {
|
||||
|
||||
fn parse_argument(&mut self, arg: &NestedMeta) -> Result<(), Diagnostic> {
|
||||
match arg {
|
||||
NestedMeta::Meta(Meta::Word(ident)) => {
|
||||
if ident == "default" {
|
||||
NestedMeta::Meta(Meta::Path(path)) => {
|
||||
if path_eq(&path, "default") {
|
||||
if self.default.is_some() {
|
||||
bail_span!(ident, "Default already set");
|
||||
bail_span!(path, "Default already set");
|
||||
}
|
||||
let expr = parse_quote!(Default::default());
|
||||
self.default = Some(expr);
|
||||
} else if ident == "optional" {
|
||||
} else if path_eq(&path, "optional") {
|
||||
self.optional = true;
|
||||
} else {
|
||||
bail_span!(ident, "Unrecognised pyarg attribute");
|
||||
bail_span!(path, "Unrecognised pyarg attribute");
|
||||
}
|
||||
}
|
||||
NestedMeta::Meta(Meta::NameValue(name_value)) => {
|
||||
if name_value.ident == "default" {
|
||||
if path_eq(&name_value.path, "default") {
|
||||
if self.default.is_some() {
|
||||
bail_span!(name_value, "Default already set");
|
||||
}
|
||||
@@ -107,7 +110,7 @@ impl ArgAttribute {
|
||||
}
|
||||
_ => bail_span!(name_value, "Expected string value for default argument"),
|
||||
}
|
||||
} else if name_value.ident == "optional" {
|
||||
} else if path_eq(&name_value.path, "optional") {
|
||||
match name_value.lit {
|
||||
Lit::Bool(ref val) => {
|
||||
self.optional = val.value;
|
||||
|
||||
@@ -12,6 +12,7 @@ mod error;
|
||||
mod compile_bytecode;
|
||||
mod from_args;
|
||||
mod pyclass;
|
||||
mod util;
|
||||
|
||||
use error::{extract_spans, Diagnostic};
|
||||
use proc_macro::TokenStream;
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
use super::Diagnostic;
|
||||
use crate::util::path_eq;
|
||||
use proc_macro2::{Span, TokenStream as TokenStream2};
|
||||
use quote::{quote, quote_spanned};
|
||||
use quote::{quote, quote_spanned, ToTokens};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use syn::{
|
||||
spanned::Spanned, Attribute, AttributeArgs, Ident, ImplItem, Index, Item, Lit, Meta, MethodSig,
|
||||
NestedMeta,
|
||||
parse_quote, spanned::Spanned, Attribute, AttributeArgs, Ident, Index, Item, Lit, Meta,
|
||||
NestedMeta, Signature,
|
||||
};
|
||||
|
||||
fn meta_to_vec(meta: Meta) -> Result<Vec<NestedMeta>, Meta> {
|
||||
match meta {
|
||||
Meta::Word(_) => Ok(Vec::new()),
|
||||
Meta::Path(_) => Ok(Vec::new()),
|
||||
Meta::List(list) => Ok(list.nested.into_iter().collect()),
|
||||
Meta::NameValue(_) => Err(meta),
|
||||
}
|
||||
@@ -53,10 +54,169 @@ impl Class {
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_method(sig: &Signature, meta: Meta) -> Result<ClassItem, Diagnostic> {
|
||||
let nesteds = meta_to_vec(meta).map_err(|meta| {
|
||||
err_span!(
|
||||
meta,
|
||||
"#[pymethod = \"...\"] cannot be a name/value, you probably meant \
|
||||
#[pymethod(name = \"...\")]",
|
||||
)
|
||||
})?;
|
||||
let mut py_name = None;
|
||||
for meta in nesteds {
|
||||
let meta = match meta {
|
||||
NestedMeta::Meta(meta) => meta,
|
||||
NestedMeta::Lit(_) => continue,
|
||||
};
|
||||
if let Meta::NameValue(name_value) = meta {
|
||||
if path_eq(&name_value.path, "name") {
|
||||
if let Lit::Str(s) = &name_value.lit {
|
||||
py_name = Some(s.value());
|
||||
} else {
|
||||
bail_span!(&sig.ident, "#[pymethod(name = ...)] must be a string");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(ClassItem::Method {
|
||||
item_ident: sig.ident.clone(),
|
||||
py_name: py_name.unwrap_or_else(|| sig.ident.to_string()),
|
||||
})
|
||||
}
|
||||
|
||||
fn extract_classmethod(sig: &Signature, meta: Meta) -> Result<ClassItem, Diagnostic> {
|
||||
let nesteds = meta_to_vec(meta).map_err(|meta| {
|
||||
err_span!(
|
||||
meta,
|
||||
"#[pyclassmethod = \"...\"] cannot be a name/value, you probably meant \
|
||||
#[pyclassmethod(name = \"...\")]",
|
||||
)
|
||||
})?;
|
||||
let mut py_name = None;
|
||||
for meta in nesteds {
|
||||
let meta = match meta {
|
||||
NestedMeta::Meta(meta) => meta,
|
||||
NestedMeta::Lit(_) => continue,
|
||||
};
|
||||
if let Meta::NameValue(name_value) = meta {
|
||||
if path_eq(&name_value.path, "name") {
|
||||
if let Lit::Str(s) = &name_value.lit {
|
||||
py_name = Some(s.value());
|
||||
} else {
|
||||
bail_span!(&sig.ident, "#[pyclassmethod(name = ...)] must be a string");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(ClassItem::ClassMethod {
|
||||
item_ident: sig.ident.clone(),
|
||||
py_name: py_name.unwrap_or_else(|| sig.ident.to_string()),
|
||||
})
|
||||
}
|
||||
|
||||
fn extract_property(sig: &Signature, meta: Meta) -> Result<ClassItem, Diagnostic> {
|
||||
let nesteds = meta_to_vec(meta).map_err(|meta| {
|
||||
err_span!(
|
||||
meta,
|
||||
"#[pyproperty = \"...\"] cannot be a name/value, you probably meant \
|
||||
#[pyproperty(name = \"...\")]"
|
||||
)
|
||||
})?;
|
||||
let mut setter = false;
|
||||
let mut py_name = None;
|
||||
for meta in nesteds {
|
||||
let meta = match meta {
|
||||
NestedMeta::Meta(meta) => meta,
|
||||
NestedMeta::Lit(_) => continue,
|
||||
};
|
||||
match meta {
|
||||
Meta::NameValue(name_value) => {
|
||||
if path_eq(&name_value.path, "name") {
|
||||
if let Lit::Str(s) = &name_value.lit {
|
||||
py_name = Some(s.value());
|
||||
} else {
|
||||
bail_span!(&sig.ident, "#[pyproperty(name = ...)] must be a string");
|
||||
}
|
||||
}
|
||||
}
|
||||
Meta::Path(path) => {
|
||||
if path_eq(&path, "setter") {
|
||||
setter = true;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
let py_name = match py_name {
|
||||
Some(py_name) => py_name,
|
||||
None => {
|
||||
let item_ident = sig.ident.to_string();
|
||||
if setter {
|
||||
if item_ident.starts_with("set_") {
|
||||
let name = &item_ident["set_".len()..];
|
||||
if name.is_empty() {
|
||||
bail_span!(
|
||||
&sig.ident,
|
||||
"A #[pyproperty(setter)] fn with a set_* name must \
|
||||
have something after \"set_\""
|
||||
)
|
||||
} else {
|
||||
name.to_string()
|
||||
}
|
||||
} else {
|
||||
bail_span!(
|
||||
&sig.ident,
|
||||
"A #[pyproperty(setter)] fn must either have a `name` \
|
||||
parameter or a fn name along the lines of \"set_*\""
|
||||
)
|
||||
}
|
||||
} else {
|
||||
item_ident
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(ClassItem::Property {
|
||||
py_name,
|
||||
item_ident: sig.ident.clone(),
|
||||
setter,
|
||||
})
|
||||
}
|
||||
|
||||
fn extract_slot(sig: &Signature, meta: Meta) -> Result<ClassItem, Diagnostic> {
|
||||
let pyslot_err = "#[pyslot] must be of the form #[pyslot] or #[pyslot(slotname)]";
|
||||
let nesteds = meta_to_vec(meta).map_err(|meta| err_span!(meta, "{}", pyslot_err))?;
|
||||
if nesteds.len() > 1 {
|
||||
return Err(Diagnostic::spanned_error("e!(#(#nesteds)*), pyslot_err));
|
||||
}
|
||||
let slot_ident = if nesteds.is_empty() {
|
||||
let ident_str = sig.ident.to_string();
|
||||
if ident_str.starts_with("tp_") {
|
||||
let slot_name = &ident_str[3..];
|
||||
proc_macro2::Ident::new(slot_name, sig.ident.span())
|
||||
} else {
|
||||
sig.ident.clone()
|
||||
}
|
||||
} else {
|
||||
match nesteds.into_iter().next().unwrap() {
|
||||
NestedMeta::Meta(Meta::Path(path)) => path
|
||||
.get_ident()
|
||||
.cloned()
|
||||
.ok_or_else(|| err_span!(path, "{}", pyslot_err))?,
|
||||
bad => bail_span!(bad, "{}", pyslot_err),
|
||||
}
|
||||
};
|
||||
Ok(ClassItem::Slot {
|
||||
slot_ident,
|
||||
item_ident: sig.ident.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
fn extract_item_from_syn(
|
||||
&mut self,
|
||||
attrs: &mut Vec<Attribute>,
|
||||
sig: &MethodSig,
|
||||
sig: &Signature,
|
||||
) -> Result<(), Diagnostic> {
|
||||
let mut attr_idxs = Vec::new();
|
||||
for (i, meta) in attrs
|
||||
@@ -65,205 +225,61 @@ impl Class {
|
||||
.enumerate()
|
||||
{
|
||||
let meta_span = meta.span();
|
||||
let name = meta.name();
|
||||
let name = match meta.path().get_ident() {
|
||||
Some(name) => name,
|
||||
None => continue,
|
||||
};
|
||||
if name == "pymethod" {
|
||||
let nesteds = meta_to_vec(meta).map_err(|meta| {
|
||||
err_span!(
|
||||
meta,
|
||||
"#[pymethod = \"...\"] cannot be a name/value, you probably meant \
|
||||
#[pymethod(name = \"...\")]",
|
||||
)
|
||||
})?;
|
||||
let mut py_name = None;
|
||||
for meta in nesteds {
|
||||
let meta = match meta {
|
||||
NestedMeta::Meta(meta) => meta,
|
||||
NestedMeta::Literal(_) => continue,
|
||||
};
|
||||
if let Meta::NameValue(name_value) = meta {
|
||||
if name_value.ident == "name" {
|
||||
if let Lit::Str(s) = &name_value.lit {
|
||||
py_name = Some(s.value());
|
||||
} else {
|
||||
bail_span!(&sig.ident, "#[pymethod(name = ...)] must be a string");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
self.add_item(
|
||||
ClassItem::Method {
|
||||
item_ident: sig.ident.clone(),
|
||||
py_name: py_name.unwrap_or_else(|| sig.ident.to_string()),
|
||||
},
|
||||
meta_span,
|
||||
)?;
|
||||
self.add_item(Self::extract_method(sig, meta)?, meta_span)?;
|
||||
attr_idxs.push(i);
|
||||
} else if name == "pyclassmethod" {
|
||||
let nesteds = meta_to_vec(meta).map_err(|meta| {
|
||||
err_span!(
|
||||
meta,
|
||||
"#[pyclassmethod = \"...\"] cannot be a name/value, you probably meant \
|
||||
#[pyclassmethod(name = \"...\")]",
|
||||
)
|
||||
})?;
|
||||
let mut py_name = None;
|
||||
for meta in nesteds {
|
||||
let meta = match meta {
|
||||
NestedMeta::Meta(meta) => meta,
|
||||
NestedMeta::Literal(_) => continue,
|
||||
};
|
||||
if let Meta::NameValue(name_value) = meta {
|
||||
if name_value.ident == "name" {
|
||||
if let Lit::Str(s) = &name_value.lit {
|
||||
py_name = Some(s.value());
|
||||
} else {
|
||||
bail_span!(
|
||||
&sig.ident,
|
||||
"#[pyclassmethod(name = ...)] must be a string"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
self.add_item(
|
||||
ClassItem::ClassMethod {
|
||||
item_ident: sig.ident.clone(),
|
||||
py_name: py_name.unwrap_or_else(|| sig.ident.to_string()),
|
||||
},
|
||||
meta_span,
|
||||
)?;
|
||||
self.add_item(Self::extract_classmethod(sig, meta)?, meta_span)?;
|
||||
attr_idxs.push(i);
|
||||
} else if name == "pyproperty" {
|
||||
let nesteds = meta_to_vec(meta).map_err(|meta| {
|
||||
err_span!(
|
||||
meta,
|
||||
"#[pyproperty = \"...\"] cannot be a name/value, you probably meant \
|
||||
#[pyproperty(name = \"...\")]"
|
||||
)
|
||||
})?;
|
||||
let mut setter = false;
|
||||
let mut py_name = None;
|
||||
for meta in nesteds {
|
||||
let meta = match meta {
|
||||
NestedMeta::Meta(meta) => meta,
|
||||
NestedMeta::Literal(_) => continue,
|
||||
};
|
||||
match meta {
|
||||
Meta::NameValue(name_value) => {
|
||||
if name_value.ident == "name" {
|
||||
if let Lit::Str(s) = &name_value.lit {
|
||||
py_name = Some(s.value());
|
||||
} else {
|
||||
bail_span!(
|
||||
&sig.ident,
|
||||
"#[pyproperty(name = ...)] must be a string"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Meta::Word(ident) => {
|
||||
if ident == "setter" {
|
||||
setter = true;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
let py_name = match py_name {
|
||||
Some(py_name) => py_name,
|
||||
None => {
|
||||
let item_ident = sig.ident.to_string();
|
||||
if setter {
|
||||
if item_ident.starts_with("set_") {
|
||||
let name = &item_ident["set_".len()..];
|
||||
if name.is_empty() {
|
||||
bail_span!(
|
||||
&sig.ident,
|
||||
"A #[pyproperty(setter)] fn with a set_* name must \
|
||||
have something after \"set_\""
|
||||
)
|
||||
} else {
|
||||
name.to_string()
|
||||
}
|
||||
} else {
|
||||
bail_span!(
|
||||
&sig.ident,
|
||||
"A #[pyproperty(setter)] fn must either have a `name` \
|
||||
parameter or a fn name along the lines of \"set_*\""
|
||||
)
|
||||
}
|
||||
} else {
|
||||
item_ident
|
||||
}
|
||||
}
|
||||
};
|
||||
self.add_item(
|
||||
ClassItem::Property {
|
||||
py_name,
|
||||
item_ident: sig.ident.clone(),
|
||||
setter,
|
||||
},
|
||||
meta_span,
|
||||
)?;
|
||||
self.add_item(Self::extract_property(sig, meta)?, meta_span)?;
|
||||
attr_idxs.push(i);
|
||||
} else if name == "pyslot" {
|
||||
let pyslot_err = "#[pyslot] must be of the form #[pyslot] or #[pyslot(slotname)]";
|
||||
let nesteds =
|
||||
meta_to_vec(meta).map_err(|meta| err_span!(meta, "{}", pyslot_err))?;
|
||||
if nesteds.len() > 1 {
|
||||
return Err(Diagnostic::spanned_error("e!(#(#nesteds)*), pyslot_err));
|
||||
}
|
||||
let slot_ident = if nesteds.is_empty() {
|
||||
let ident_str = sig.ident.to_string();
|
||||
if ident_str.starts_with("tp_") {
|
||||
let slot_name = &ident_str[3..];
|
||||
proc_macro2::Ident::new(slot_name, sig.ident.span())
|
||||
} else {
|
||||
sig.ident.clone()
|
||||
}
|
||||
} else {
|
||||
match nesteds.into_iter().next().unwrap() {
|
||||
NestedMeta::Meta(Meta::Word(ident)) => ident,
|
||||
bad => bail_span!(bad, "{}", pyslot_err),
|
||||
}
|
||||
};
|
||||
self.add_item(
|
||||
ClassItem::Slot {
|
||||
slot_ident,
|
||||
item_ident: sig.ident.clone(),
|
||||
},
|
||||
meta_span,
|
||||
)?;
|
||||
self.add_item(Self::extract_slot(sig, meta)?, meta_span)?;
|
||||
attr_idxs.push(i);
|
||||
}
|
||||
}
|
||||
for idx in attr_idxs {
|
||||
attrs.remove(idx);
|
||||
let mut i = 0;
|
||||
let mut attr_idxs = &*attr_idxs;
|
||||
attrs.retain(|_| {
|
||||
let drop = attr_idxs.first().copied() == Some(i);
|
||||
if drop {
|
||||
attr_idxs = &attr_idxs[1..];
|
||||
}
|
||||
i += 1;
|
||||
!drop
|
||||
});
|
||||
for (i, idx) in attr_idxs.iter().enumerate() {
|
||||
attrs.remove(idx - i);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn impl_pyimpl(_attr: AttributeArgs, item: Item) -> Result<TokenStream2, Diagnostic> {
|
||||
let mut imp = if let Item::Impl(imp) = item {
|
||||
imp
|
||||
} else {
|
||||
return Ok(quote!(#item));
|
||||
};
|
||||
struct ItemSig<'a> {
|
||||
attrs: &'a mut Vec<Attribute>,
|
||||
sig: &'a Signature,
|
||||
}
|
||||
|
||||
fn extract_impl_items(
|
||||
attr: AttributeArgs,
|
||||
mut items: Vec<ItemSig>,
|
||||
) -> Result<TokenStream2, Diagnostic> {
|
||||
let mut diagnostics: Vec<Diagnostic> = Vec::new();
|
||||
|
||||
let mut class = Class::default();
|
||||
|
||||
for item in imp.items.iter_mut() {
|
||||
if let ImplItem::Method(meth) = item {
|
||||
push_diag_result!(
|
||||
diagnostics,
|
||||
class.extract_item_from_syn(&mut meth.attrs, &meth.sig),
|
||||
);
|
||||
}
|
||||
for item in items.iter_mut() {
|
||||
push_diag_result!(
|
||||
diagnostics,
|
||||
class.extract_item_from_syn(&mut item.attrs, item.sig),
|
||||
);
|
||||
}
|
||||
let ty = &imp.self_ty;
|
||||
|
||||
let mut properties: HashMap<&str, (Option<&Ident>, Option<&Ident>)> = HashMap::new();
|
||||
for item in class.items.iter() {
|
||||
if let ClassItem::Property {
|
||||
@@ -346,19 +362,88 @@ pub fn impl_pyimpl(_attr: AttributeArgs, item: Item) -> Result<TokenStream2, Dia
|
||||
|
||||
Diagnostic::from_vec(diagnostics)?;
|
||||
|
||||
let ret = quote! {
|
||||
#imp
|
||||
impl ::rustpython_vm::pyobject::PyClassImpl for #ty {
|
||||
fn impl_extend_class(
|
||||
ctx: &::rustpython_vm::pyobject::PyContext,
|
||||
class: &::rustpython_vm::obj::objtype::PyClassRef,
|
||||
) {
|
||||
#(#methods)*
|
||||
#(#properties)*
|
||||
let mut withs = Vec::new();
|
||||
|
||||
for attr in attr {
|
||||
match attr {
|
||||
NestedMeta::Meta(Meta::List(syn::MetaList { path, nested, .. }))
|
||||
if path_eq(&path, "with") =>
|
||||
{
|
||||
for meta in nested {
|
||||
match meta {
|
||||
NestedMeta::Meta(Meta::Path(path)) => {
|
||||
withs.push(quote! {
|
||||
<Self as #path>::__extend_py_class(ctx, class);
|
||||
});
|
||||
}
|
||||
meta => bail_span!(meta, "#[pyimpl(with(...))] arguments should be paths"),
|
||||
}
|
||||
}
|
||||
}
|
||||
attr => bail_span!(attr, "Unknown pyimpl attribute"),
|
||||
}
|
||||
};
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
Ok(quote! {
|
||||
#(#methods)*
|
||||
#(#properties)*
|
||||
#(#withs)*
|
||||
})
|
||||
}
|
||||
|
||||
pub fn impl_pyimpl(attr: AttributeArgs, item: Item) -> Result<TokenStream2, Diagnostic> {
|
||||
match item {
|
||||
Item::Impl(mut imp) => {
|
||||
let items = imp
|
||||
.items
|
||||
.iter_mut()
|
||||
.filter_map(|item| match item {
|
||||
syn::ImplItem::Method(syn::ImplItemMethod { attrs, sig, .. }) => {
|
||||
Some(ItemSig { attrs, sig })
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
let extend_impl = extract_impl_items(attr, items)?;
|
||||
let ty = &imp.self_ty;
|
||||
let ret = quote! {
|
||||
#imp
|
||||
impl ::rustpython_vm::pyobject::PyClassImpl for #ty {
|
||||
fn impl_extend_class(
|
||||
ctx: &::rustpython_vm::pyobject::PyContext,
|
||||
class: &::rustpython_vm::obj::objtype::PyClassRef,
|
||||
) {
|
||||
#extend_impl
|
||||
}
|
||||
}
|
||||
};
|
||||
Ok(ret)
|
||||
}
|
||||
Item::Trait(mut trai) => {
|
||||
let items = trai
|
||||
.items
|
||||
.iter_mut()
|
||||
.filter_map(|item| match item {
|
||||
syn::TraitItem::Method(syn::TraitItemMethod { attrs, sig, .. }) => {
|
||||
Some(ItemSig { attrs, sig })
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
let extend_impl = extract_impl_items(attr, items)?;
|
||||
let item = parse_quote! {
|
||||
fn __extend_py_class(
|
||||
ctx: &::rustpython_vm::pyobject::PyContext,
|
||||
class: &::rustpython_vm::obj::objtype::PyClassRef,
|
||||
) {
|
||||
#extend_impl
|
||||
}
|
||||
};
|
||||
trai.items.push(item);
|
||||
Ok(trai.into_token_stream())
|
||||
}
|
||||
item => Ok(quote!(#item)),
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_class_def(
|
||||
@@ -371,7 +456,7 @@ fn generate_class_def(
|
||||
for attr in attr {
|
||||
if let NestedMeta::Meta(meta) = attr {
|
||||
if let Meta::NameValue(name_value) = meta {
|
||||
if name_value.ident == "name" {
|
||||
if path_eq(&name_value.path, "name") {
|
||||
if let Lit::Str(s) = name_value.lit {
|
||||
class_name = Some(s.value());
|
||||
} else {
|
||||
|
||||
3
derive/src/util.rs
Normal file
3
derive/src/util.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
pub fn path_eq(path: &syn::Path, s: &str) -> bool {
|
||||
path.get_ident().map_or(false, |id| id == s)
|
||||
}
|
||||
@@ -14,7 +14,6 @@ lalrpop="0.17"
|
||||
[dependencies]
|
||||
lalrpop-util="0.17"
|
||||
log="0.4.1"
|
||||
regex = "1"
|
||||
num-bigint = "0.2"
|
||||
num-traits = "0.2"
|
||||
unic-emoji-char = "0.9"
|
||||
|
||||
@@ -2,8 +2,10 @@ use crate::function::OptionalArg;
|
||||
use crate::pyobject::{IdProtocol, PyObjectRef, PyRef, PyResult, PyValue};
|
||||
use crate::VirtualMachine;
|
||||
|
||||
// TODO: pyimpl compose pyslot(descr_get) and pymethod(__get__) from this trait
|
||||
#[pyimpl]
|
||||
pub trait PyBuiltinDescriptor: PyValue {
|
||||
#[pymethod(name = "__get__")]
|
||||
#[pyslot(descr_get)]
|
||||
fn get(
|
||||
zelf: PyRef<Self>,
|
||||
obj: PyObjectRef,
|
||||
|
||||
@@ -87,7 +87,7 @@ impl PyBuiltinDescriptor for PyBuiltinMethod {
|
||||
}
|
||||
}
|
||||
|
||||
#[pyimpl]
|
||||
#[pyimpl(with(PyBuiltinDescriptor))]
|
||||
impl PyBuiltinMethod {
|
||||
#[pymethod(name = "__call__")]
|
||||
pub fn call(&self, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult {
|
||||
@@ -98,8 +98,4 @@ impl PyBuiltinMethod {
|
||||
pub fn init(context: &PyContext) {
|
||||
PyBuiltinFunction::extend_class(context, &context.types.builtin_function_or_method_type);
|
||||
PyBuiltinMethod::extend_class(context, &context.types.method_descriptor_type);
|
||||
extend_class!(context, context.types.method_descriptor_type, {
|
||||
"__get__" => context.new_method(PyBuiltinMethod::get),
|
||||
(slot descr_get) => PyBuiltinMethod::get,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ impl PyBuiltinDescriptor for PyClassMethod {
|
||||
}
|
||||
}
|
||||
|
||||
#[pyimpl]
|
||||
#[pyimpl(with(PyBuiltinDescriptor))]
|
||||
impl PyClassMethod {
|
||||
#[pyslot]
|
||||
fn tp_new(
|
||||
@@ -81,8 +81,4 @@ impl PyClassMethod {
|
||||
|
||||
pub(crate) fn init(context: &PyContext) {
|
||||
PyClassMethod::extend_class(context, &context.types.classmethod_type);
|
||||
extend_class!(context, context.types.classmethod_type, {
|
||||
"__get__" => context.new_method(PyClassMethod::get),
|
||||
(slot descr_get) => PyClassMethod::get,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -240,7 +240,7 @@ impl PyValue for PyFunction {
|
||||
}
|
||||
}
|
||||
|
||||
#[pyimpl]
|
||||
#[pyimpl(with(PyBuiltinDescriptor))]
|
||||
impl PyFunction {
|
||||
#[pymethod(name = "__call__")]
|
||||
fn call(zelf: PyObjectRef, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult {
|
||||
@@ -289,10 +289,6 @@ impl PyValue for PyBoundMethod {
|
||||
pub fn init(context: &PyContext) {
|
||||
let function_type = &context.types.function_type;
|
||||
PyFunction::extend_class(context, function_type);
|
||||
extend_class!(context, function_type, {
|
||||
"__get__" => context.new_method(PyFunction::get),
|
||||
(slot descr_get) => PyFunction::get,
|
||||
});
|
||||
|
||||
let method_type = &context.types.bound_method_type;
|
||||
extend_class!(context, method_type, {
|
||||
|
||||
@@ -46,7 +46,7 @@ impl PyBuiltinDescriptor for PyReadOnlyProperty {
|
||||
}
|
||||
}
|
||||
|
||||
#[pyimpl]
|
||||
#[pyimpl(with(PyBuiltinDescriptor))]
|
||||
impl PyReadOnlyProperty {}
|
||||
|
||||
/// Property attribute.
|
||||
@@ -129,7 +129,7 @@ impl PyBuiltinDescriptor for PyProperty {
|
||||
}
|
||||
}
|
||||
|
||||
#[pyimpl]
|
||||
#[pyimpl(with(PyBuiltinDescriptor))]
|
||||
impl PyProperty {
|
||||
#[pyslot]
|
||||
fn tp_new(cls: PyClassRef, args: PropertyArgs, vm: &VirtualMachine) -> PyResult<PyPropertyRef> {
|
||||
@@ -318,15 +318,8 @@ impl<'a> PropertyBuilder<'a> {
|
||||
|
||||
pub fn init(context: &PyContext) {
|
||||
PyReadOnlyProperty::extend_class(context, &context.types.readonly_property_type);
|
||||
extend_class!(context, context.types.readonly_property_type, {
|
||||
"__get__" => context.new_method(PyReadOnlyProperty::get),
|
||||
(slot descr_get) => PyReadOnlyProperty::get,
|
||||
});
|
||||
|
||||
PyProperty::extend_class(context, &context.types.property_type);
|
||||
extend_class!(context, context.types.property_type, {
|
||||
"__get__" => context.new_method(PyProperty::get),
|
||||
});
|
||||
|
||||
// This is a bit unfortunate, but this instance attribute overlaps with the
|
||||
// class __doc__ string..
|
||||
|
||||
@@ -28,7 +28,7 @@ impl PyBuiltinDescriptor for PyStaticMethod {
|
||||
}
|
||||
}
|
||||
|
||||
#[pyimpl]
|
||||
#[pyimpl(with(PyBuiltinDescriptor))]
|
||||
impl PyStaticMethod {
|
||||
#[pyslot]
|
||||
fn tp_new(
|
||||
@@ -45,8 +45,4 @@ impl PyStaticMethod {
|
||||
|
||||
pub fn init(context: &PyContext) {
|
||||
PyStaticMethod::extend_class(context, &context.types.staticmethod_type);
|
||||
extend_class!(context, context.types.staticmethod_type, {
|
||||
"__get__" => context.new_method(PyStaticMethod::get),
|
||||
(slot descr_get) => PyStaticMethod::get,
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user