From 561f4ee779e093cbb0027bf92d208b35a8d960f0 Mon Sep 17 00:00:00 2001 From: Noah <33094578+coolreader18@users.noreply.github.com> Date: Mon, 1 Feb 2021 12:06:18 -0600 Subject: [PATCH] Allow tuple structs for derive(FromArgs) --- derive/src/from_args.rs | 40 +++++++++++++++++++++------------------- vm/src/stdlib/os.rs | 39 +++++++++++---------------------------- 2 files changed, 32 insertions(+), 47 deletions(-) diff --git a/derive/src/from_args.rs b/derive/src/from_args.rs index 876491f6f..ca48ca744 100644 --- a/derive/src/from_args.rs +++ b/derive/src/from_args.rs @@ -1,10 +1,8 @@ use crate::util::path_eq; use crate::Diagnostic; use proc_macro2::TokenStream as TokenStream2; -use quote::quote; -use syn::{ - parse_quote, Attribute, Data, DeriveInput, Expr, Field, Fields, Ident, Lit, Meta, NestedMeta, -}; +use quote::{quote, ToTokens}; +use syn::{parse_quote, Attribute, Data, DeriveInput, Expr, Field, Ident, Lit, Meta, NestedMeta}; /// The kind of the python parameter, this corresponds to the value of Parameter.kind /// (https://docs.python.org/3/library/inspect.html#inspect.Parameter.kind) @@ -121,7 +119,7 @@ impl ArgAttribute { } } -fn generate_field(field: &Field) -> Result { +fn generate_field((i, field): (usize, &Field)) -> Result { let mut pyarg_attrs = field .attrs .iter() @@ -139,19 +137,26 @@ fn generate_field(field: &Field) -> Result { bail_span!(field, "Multiple pyarg attributes on field"); }; - let name = field.ident.as_ref().unwrap(); - let namestring = name.to_string(); - if namestring.starts_with("_phantom") { + let name = field.ident.as_ref(); + let namestring = name.map(Ident::to_string); + if matches!(&namestring, Some(s) if s.starts_with("_phantom")) { return Ok(quote! { #name: ::std::marker::PhantomData, }); } + let fieldname = match name { + Some(id) => id.to_token_stream(), + None => syn::Index::from(i).into_token_stream(), + }; if let ParameterKind::Flatten = attr.kind { return Ok(quote! { - #name: ::rustpython_vm::function::FromArgs::from_args(vm, args)?, + #fieldname: ::rustpython_vm::function::FromArgs::from_args(vm, args)?, }); } - let pyname = attr.name.unwrap_or(namestring); + let pyname = attr + .name + .or(namestring) + .ok_or_else(|| err_span!(field, "field in tuple struct must have name attribute"))?; let middle = quote! { .map(|x| ::rustpython_vm::pyobject::TryFromObject::try_from_object(vm, x)).transpose()? }; @@ -179,17 +184,17 @@ fn generate_field(field: &Field) -> Result { let file_output = match attr.kind { ParameterKind::PositionalOnly => { quote! { - #name: args.take_positional()#middle#ending, + #fieldname: args.take_positional()#middle#ending, } } ParameterKind::PositionalOrKeyword => { quote! { - #name: args.take_positional_keyword(#pyname)#middle#ending, + #fieldname: args.take_positional_keyword(#pyname)#middle#ending, } } ParameterKind::KeywordOnly => { quote! { - #name: args.take_keyword(#pyname)#middle#ending, + #fieldname: args.take_keyword(#pyname)#middle#ending, } } ParameterKind::Flatten => unreachable!(), @@ -199,15 +204,12 @@ fn generate_field(field: &Field) -> Result { pub fn impl_from_args(input: DeriveInput) -> Result { let fields = match input.data { - Data::Struct(syn::DataStruct { - fields: Fields::Named(fields), - .. - }) => fields - .named + Data::Struct(syn::DataStruct { fields, .. }) => fields .iter() + .enumerate() .map(generate_field) .collect::>()?, - _ => bail_span!(input, "FromArgs input must be a struct with named fields"), + _ => bail_span!(input, "FromArgs input must be a struct"), }; let name = input.ident; diff --git a/vm/src/stdlib/os.rs b/vm/src/stdlib/os.rs index fbc273ba9..55de9fd39 100644 --- a/vm/src/stdlib/os.rs +++ b/vm/src/stdlib/os.rs @@ -262,10 +262,7 @@ impl FromArgs for DirFd { } #[derive(FromArgs)] -struct FollowSymlinks { - #[pyarg(named, default = "true")] - follow_symlinks: bool, -} +struct FollowSymlinks(#[pyarg(named, name = "follow_symlinks", default = "true")] bool); #[cfg(unix)] use posix::bytes_as_osstr; @@ -610,7 +607,7 @@ mod _os { action: fn(fs::Metadata) -> bool, vm: &VirtualMachine, ) -> PyResult { - let meta = fs_metadata(self.entry.path(), follow_symlinks.follow_symlinks) + let meta = fs_metadata(self.entry.path(), follow_symlinks.0) .map_err(|err| err.into_pyexception(vm))?; Ok(action(meta)) } @@ -771,14 +768,7 @@ mod _os { #[pyfunction] fn lstat(file: Either, dir_fd: DirFd, vm: &VirtualMachine) -> PyResult { - super::platform::stat( - file, - dir_fd, - FollowSymlinks { - follow_symlinks: false, - }, - vm, - ) + super::platform::stat(file, dir_fd, FollowSymlinks(false), vm) } #[pyfunction] @@ -987,7 +977,7 @@ mod _os { path, &acc.into(), &modif.into(), - if _follow_symlinks.follow_symlinks { + if _follow_symlinks.0 { nix::sys::stat::UtimensatFlags::FollowSymlink } else { nix::sys::stat::UtimensatFlags::NoFollowSymlink @@ -1494,10 +1484,7 @@ mod posix { use std::os::redox::fs::MetadataExt; let meta = match file { - Either::A(path) => fs_metadata( - make_path(vm, &path, &dir_fd)?, - follow_symlinks.follow_symlinks, - ), + Either::A(path) => fs_metadata(make_path(vm, &path, &dir_fd)?, follow_symlinks.0), Either::B(fno) => { let file = rust_file(fno); let res = file.metadata(); @@ -1593,7 +1580,7 @@ mod posix { return Err(vm.new_os_error(String::from("Specified gid is not valid."))); }; - let flag = if follow_symlinks.follow_symlinks { + let flag = if follow_symlinks.0 { nix::unistd::FchownatFlags::FollowSymlink } else { nix::unistd::FchownatFlags::NoFollowSymlink @@ -1619,9 +1606,7 @@ mod posix { uid, gid, DirFd(None), - FollowSymlinks { - follow_symlinks: false, - }, + FollowSymlinks(false), vm, ) } @@ -1634,9 +1619,7 @@ mod posix { uid, gid, DirFd(None), - FollowSymlinks { - follow_symlinks: true, - }, + FollowSymlinks(true), vm, ) } @@ -1751,7 +1734,7 @@ mod posix { let path = make_path(vm, &path, &dir_fd)?; let body = move || { use std::os::unix::fs::PermissionsExt; - let meta = fs_metadata(path, follow_symlinks.follow_symlinks)?; + let meta = fs_metadata(path, follow_symlinks.0)?; let mut permissions = meta.permissions(); permissions.set_mode(mode); fs::set_permissions(path, permissions) @@ -2616,7 +2599,7 @@ mod nt { let get_stats = move || -> io::Result { let meta = match file { - Either::A(path) => fs_metadata(path.path, follow_symlinks.follow_symlinks)?, + Either::A(path) => fs_metadata(path.path, follow_symlinks.0)?, Either::B(fno) => { let f = rust_file(fno); let meta = f.metadata()?; @@ -2662,7 +2645,7 @@ mod nt { ) -> PyResult<()> { const S_IWRITE: u32 = 128; let path = make_path(vm, &path, &dir_fd)?; - let metadata = if follow_symlinks.follow_symlinks { + let metadata = if follow_symlinks.0 { fs::metadata(path) } else { fs::symlink_metadata(path)