Skip to content
Merged
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
2 changes: 1 addition & 1 deletion crates/cgp-macro-lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ description = """
default = []

[dependencies]
syn = { version = "2.0.95", features = [ "full", "extra-traits" ] }
syn = { version = "2.0.95", features = [ "full", "extra-traits", "visit" ] }
quote = "1.0.38"
proc-macro2 = "1.0.92"
prettyplease = "0.2.27"
Expand Down
24 changes: 0 additions & 24 deletions crates/cgp-macro-lib/src/cgp_fn/constraint.rs

This file was deleted.

9 changes: 6 additions & 3 deletions crates/cgp-macro-lib/src/cgp_fn/derive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,18 @@ pub fn derive_cgp_fn(trait_ident: &Ident, mut item_fn: ItemFn) -> syn::Result<To
}
};

let visibility = item_fn.vis.clone();
item_fn.vis = Visibility::Inherited;

let implicit_args = extract_implicits_args(&receiver, &mut item_fn.sig.inputs)?;

let attributes = parse_function_attributes(&mut item_fn.attrs)?;

item_fn.vis = Visibility::Inherited;

inject_implicit_args(&implicit_args, &mut item_fn.block)?;

let generics = mem::take(&mut item_fn.sig.generics);

let item_trait = derive_item_trait(trait_ident, &item_fn, &generics, &attributes)?;
let mut item_trait = derive_item_trait(trait_ident, &item_fn, &generics, &attributes)?;

let item_impl = derive_item_impl(
trait_ident,
Expand All @@ -40,6 +41,8 @@ pub fn derive_cgp_fn(trait_ident: &Ident, mut item_fn: ItemFn) -> syn::Result<To
&attributes,
)?;

item_trait.vis = visibility.clone();

let output = quote! {
#item_trait

Expand Down
3 changes: 2 additions & 1 deletion crates/cgp-macro-lib/src/cgp_fn/fn_body.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ pub fn inject_implicit_args(args: &[ImplicitArgField], body: &mut Block) -> syn:

pub fn inject_implicit_arg(arg: &ImplicitArgField, body: &mut Block) -> syn::Result<()> {
let field_name = &arg.field_name;
let arg_type = &arg.arg_type;

let field_symbol = symbol_from_string(&field_name.to_string());

Expand All @@ -30,7 +31,7 @@ pub fn inject_implicit_arg(arg: &ImplicitArgField, body: &mut Block) -> syn::Res
let call_expr = extend_call_expr(call_expr, &arg.field_mode, &arg.field_mut);

let statement = parse2(quote! {
let #field_name = #call_expr;
let #field_name: #arg_type = #call_expr;
})?;

body.stmts.insert(0, statement);
Expand Down
32 changes: 22 additions & 10 deletions crates/cgp-macro-lib/src/cgp_fn/item_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,23 @@ pub fn derive_item_impl(
.params
.insert(0, parse2(quote! { __Context__ })?);

let mut bounds: Punctuated<TypeParamBound, Plus> = Punctuated::default();
bounds.extend(attributes.extend.clone());
{
let mut bounds: Punctuated<TypeParamBound, Plus> = Punctuated::default();
bounds.extend(attributes.extend.clone());

for import in attributes.uses.iter() {
bounds.push(parse2(quote! { #import })?);
}

for import in attributes.uses.iter() {
bounds.push(parse2(quote! { #import })?);
if !bounds.is_empty() {
item_impl
.generics
.make_where_clause()
.predicates
.push(parse2(quote! {
Self: #bounds
})?);
}
}

{
Expand All @@ -56,18 +68,18 @@ pub fn derive_item_impl(
}

if !attributes.use_type.is_empty() {
for use_type in attributes.use_type.iter() {
bounds.push(parse2(use_type.trait_path.to_token_stream())?);
}

item_impl = parse2(substitute_abstract_type(
&quote! { Self },
&attributes.use_type,
item_impl.to_token_stream(),
))?;
}

if !bounds.is_empty() {
let mut bounds: Punctuated<TypeParamBound, Plus> = Punctuated::default();

for use_type in attributes.use_type.iter() {
bounds.push(parse2(use_type.trait_path.to_token_stream())?);
}

item_impl
.generics
.make_where_clause()
Expand Down
22 changes: 8 additions & 14 deletions crates/cgp-macro-lib/src/cgp_fn/item_trait.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
use quote::{ToTokens, quote};
use syn::punctuated::Punctuated;
use syn::token::Plus;
use syn::{Generics, Ident, ItemFn, ItemTrait, TraitItemFn, TypeParamBound, parse2};
use syn::{Generics, Ident, ItemFn, ItemTrait, TraitItemFn, parse2};

use crate::cgp_fn::{FunctionAttributes, substitute_abstract_type};

Expand All @@ -26,25 +24,21 @@ pub fn derive_item_trait(
}
})?;

let mut bounds: Punctuated<TypeParamBound, Plus> = Punctuated::default();

for extend in &attributes.extend {
bounds.push(extend.clone());
}
item_trait.supertraits.extend(attributes.extend.clone());

if !attributes.use_type.is_empty() {
for use_type in attributes.use_type.iter() {
bounds.push(parse2(use_type.trait_path.to_token_stream())?);
}

item_trait = parse2(substitute_abstract_type(
&quote! { Self },
&attributes.use_type,
item_trait.to_token_stream(),
))?;
}

item_trait.supertraits.extend(bounds);
for use_type in attributes.use_type.iter() {
item_trait
.supertraits
.push(parse2(use_type.trait_path.to_token_stream())?);
}
}

Ok(item_trait)
}
1 change: 0 additions & 1 deletion crates/cgp-macro-lib/src/cgp_fn/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
mod attributes;
mod constraint;
mod derive;
mod fn_body;
mod item_impl;
Expand Down
109 changes: 80 additions & 29 deletions crates/cgp-macro-lib/src/cgp_fn/parse_implicits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ use std::mem;

use syn::punctuated::Punctuated;
use syn::token::Comma;
use syn::{Attribute, FnArg, Meta, Pat, PatType, Receiver};
use syn::visit::{self, Visit};
use syn::{Attribute, FnArg, Meta, Pat, PatIdent, PatType, Receiver};

use crate::cgp_fn::ImplicitArgField;
use crate::derive_getter::parse_field_type;
Expand All @@ -11,63 +12,113 @@ pub fn extract_implicits_args(
receiver: &Receiver,
args: &mut Punctuated<FnArg, Comma>,
) -> syn::Result<Vec<ImplicitArgField>> {
let mut implicit_args = Vec::new();

let process_args = mem::take(args);
let implicit_fn_args = extract_implicit_args(args);

for mut arg in process_args.into_iter() {
if let Some(implicit_arg) = try_parse_implicit_arg(receiver, &mut arg)? {
implicit_args.push(implicit_arg);
} else {
args.push(arg);
}
if receiver.mutability.is_some() && implicit_fn_args.len() > 1 {
return Err(syn::Error::new_spanned(
&args,
"Only one mutable implicit argument is allowed when self is mutable",
));
}

Ok(implicit_args)
}
let mut implicit_args = Vec::new();

pub fn try_parse_implicit_arg(
receiver: &Receiver,
arg: &mut FnArg,
) -> syn::Result<Option<ImplicitArgField>> {
if let FnArg::Typed(arg) = arg {
let attrs = mem::take(&mut arg.attrs);
for attr in attrs {
if is_implicit_attr(&attr) {
let spec = parse_implicit_arg(receiver, arg)?;
return Ok(Some(spec));
} else {
arg.attrs.push(attr);
}
}
for arg in implicit_fn_args {
let spec = parse_implicit_arg(receiver, &arg)?;
implicit_args.push(spec);
}

Ok(None)
Ok(implicit_args)
}

pub fn parse_implicit_arg(receiver: &Receiver, arg: &PatType) -> syn::Result<ImplicitArgField> {
let Pat::Ident(pat_ident) = &*arg.pat else {
return Err(syn::Error::new_spanned(&arg.pat, "Expected an identifier"));
};

let arg_type = arg.ty.as_ref();
if has_mut_pattern(&arg.pat) {
return Err(syn::Error::new_spanned(
&arg.pat,
"Mutable variables are not allowed in implicit arguments. (Explicitly clone a `&` reference if you want a mutable local copy of the value)",
));
}

let arg_type = arg.ty.as_ref().clone();

let field_mut = receiver.mutability;

let (field_type, field_mode) = parse_field_type(arg_type, &field_mut)?;
let (field_type, field_mode) = parse_field_type(&arg_type, &field_mut)?;

let spec = ImplicitArgField {
field_name: pat_ident.ident.clone(),
field_type,
field_mut,
field_mode,
arg_type,
};

Ok(spec)
}

pub fn extract_implicit_args(args: &mut Punctuated<FnArg, Comma>) -> Vec<PatType> {
let mut implicit_args = Vec::new();

let process_args = mem::take(args);

for arg in process_args.into_iter() {
if let FnArg::Typed(mut arg) = arg {
if is_implicit_arg(&mut arg) {
implicit_args.push(arg);
} else {
args.push(FnArg::Typed(arg));
}
} else {
args.push(arg);
}
}

implicit_args
}

pub fn is_implicit_arg(arg: &mut PatType) -> bool {
let mut res = false;

let attrs = mem::take(&mut arg.attrs);

for attr in attrs {
if is_implicit_attr(&attr) {
res = true;
} else {
arg.attrs.push(attr);
}
}

res
}

pub fn is_implicit_attr(attr: &Attribute) -> bool {
match &attr.meta {
Meta::Path(path) => path.is_ident("implicit"),
_ => false,
}
}

pub fn has_mut_pattern(pat: &Pat) -> bool {
let mut checker = MutChecker { has_mut: false };
checker.visit_pat(pat);
checker.has_mut
}

struct MutChecker {
has_mut: bool,
}

impl<'ast> Visit<'ast> for MutChecker {
fn visit_pat_ident(&mut self, node: &'ast PatIdent) {
if node.mutability.is_some() {
self.has_mut = true;
}
// Continue walking through the rest of the pattern
visit::visit_pat_ident(self, node);
}
}
1 change: 1 addition & 0 deletions crates/cgp-macro-lib/src/cgp_fn/spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pub struct ImplicitArgField {
pub field_type: Type,
pub field_mut: Option<Mut>,
pub field_mode: FieldMode,
pub arg_type: Type,
}

#[derive(Default)]
Expand Down
12 changes: 10 additions & 2 deletions crates/cgp-macro-lib/src/cgp_fn/substitute_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pub fn substitute_abstract_type(
) -> TokenStream {
let mut out = TokenStream::new();
let mut last_token_was_colon = false;
let mut last_two_tokens_was_colon = false;

for token_tree in body.into_iter() {
let token_is_colon = if let TokenTree::Punct(punct) = &token_tree
Expand All @@ -29,7 +30,7 @@ pub fn substitute_abstract_type(
let mut replaced_ident = false;

for type_spec in type_specs {
if !last_token_was_colon
if !last_two_tokens_was_colon
&& let Some(replacement_ident) = type_spec.replace_ident(&ident)
{
let trait_path = &type_spec.trait_path;
Expand All @@ -53,7 +54,14 @@ pub fn substitute_abstract_type(
}
}

last_token_was_colon = token_is_colon;
if token_is_colon {
if last_token_was_colon {
last_token_was_colon = true;
} else {
last_token_was_colon = true;
last_two_tokens_was_colon = false;
}
}
}

out
Expand Down
2 changes: 1 addition & 1 deletion crates/cgp-macro-lib/src/derive_getter/blanket.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ pub fn derive_blanket_impl(

let constraint = derive_getter_constraint(
&field.field_type,
&field.field_mut,
&field.receiver_mut,
&field.field_mode,
quote! { #field_symbol },
&field_assoc_type.as_ref().map(|item| item.ident.clone()),
Expand Down
2 changes: 1 addition & 1 deletion crates/cgp-macro-lib/src/derive_getter/getter_field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ pub struct GetterField {
pub field_name: Ident,
pub field_type: Type,
pub return_type: Type,
pub field_mut: Option<Mut>,
pub receiver_mut: Option<Mut>,
pub phantom_arg_type: Option<Type>,
pub field_mode: FieldMode,
pub receiver_mode: ReceiverMode,
Expand Down
Loading
Loading