Skip to content

Commit

Permalink
add index() to KvParser, clone attributes when extracting attributes …
Browse files Browse the repository at this point in the history
…from a signature to resolve double immutable borrow
  • Loading branch information
you-win committed Aug 10, 2023
1 parent be0ceee commit 5f70989
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 134 deletions.
189 changes: 56 additions & 133 deletions godot-macros/src/godot_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@ use proc_macro2::{Ident, TokenStream};
use quote::quote;
use quote::spanned::Spanned;
use venial::{
Attribute, AttributeValue, Constant, Declaration, Error, FnParam, Function, Impl, ImplMember,
TyExpr,
Attribute, Constant, Declaration, Error, FnParam, Function, Impl, ImplMember, TyExpr,
};

pub fn transform(input_decl: Declaration) -> Result<TokenStream, Error> {
Expand Down Expand Up @@ -50,8 +49,8 @@ pub fn transform(input_decl: Declaration) -> Result<TokenStream, Error> {
/// Attribute for user-declared function
enum BoundAttrType {
Func { rename: Option<String> },
Signal(AttributeValue),
Const(AttributeValue),
Signal,
Const,
}

struct BoundAttr {
Expand All @@ -66,8 +65,6 @@ impl BoundAttr {
}
}

type Signal = Function;

/// Codegen for `#[godot_api] impl MyType`
fn transform_inherent_impl(mut decl: Impl) -> Result<TokenStream, Error> {
let class_name = util::validate_impl(&decl, None, "godot_api")?;
Expand Down Expand Up @@ -203,7 +200,7 @@ fn transform_inherent_impl(mut decl: Impl) -> Result<TokenStream, Error> {
Ok(result)
}

fn process_godot_fns(decl: &mut Impl) -> Result<(Vec<FuncDefinition>, Vec<Signal>), Error> {
fn process_godot_fns(decl: &mut Impl) -> Result<(Vec<FuncDefinition>, Vec<Function>), Error> {
let mut func_definitions = vec![];
let mut signal_signatures = vec![];

Expand Down Expand Up @@ -239,7 +236,7 @@ fn process_godot_fns(decl: &mut Impl) -> Result<(Vec<FuncDefinition>, Vec<Signal
let sig = util::reduce_to_signature(method);
func_definitions.push(FuncDefinition { func: sig, rename });
}
BoundAttrType::Signal(ref _attr_val) => {
BoundAttrType::Signal => {
if method.return_ty.is_some() {
return attr.bail("return types are not supported", method);
}
Expand All @@ -248,7 +245,7 @@ fn process_godot_fns(decl: &mut Impl) -> Result<(Vec<FuncDefinition>, Vec<Signal
signal_signatures.push(sig.clone());
removed_indexes.push(index);
}
BoundAttrType::Const(_) => {
BoundAttrType::Const => {
return attr.bail(
"#[constant] can only be used on associated constant",
method,
Expand Down Expand Up @@ -283,10 +280,10 @@ fn process_godot_constants(decl: &mut Impl) -> Result<Vec<Constant>, Error> {
BoundAttrType::Func { .. } => {
return bail!(constant, "#[func] can only be used on functions")
}
BoundAttrType::Signal(_) => {
BoundAttrType::Signal => {
return bail!(constant, "#[signal] can only be used on functions")
}
BoundAttrType::Const(_) => {
BoundAttrType::Const => {
if constant.initializer.is_none() {
return bail!(constant, "exported constant must have initializer");
}
Expand All @@ -306,133 +303,59 @@ fn extract_attributes<T>(
where
for<'a> &'a T: Spanned,
{
let mut found = None;
for (index, attr) in attributes.iter().enumerate() {
let attr_name = attr
.get_single_path_segment()
.expect("get_single_path_segment");

// const FUNC: &str = "func";
// const SIGNAL: &str = "signal";
// const CONSTANT: &str = "constant";

// let mut name_and_parser = None;

// for name in [FUNC, SIGNAL, CONSTANT] {
// if let Some(valid_parser) = KvParser::parse(attributes, name)? {
// name_and_parser = Some((name, valid_parser));
// break;
// }
// }

// let Some((name, mut parser)) = name_and_parser else {
// continue
// };

// let new_found = match name {
// FUNC => {
// // TODO you-win (August 8, 2023): handle default values here as well?

// let rename = parser.handle_expr("rename")?.map(|value| value.to_string());

// parser.finish()?;

// Some(BoundAttr {
// attr_name: attr_name.clone(),
// index,
// ty: BoundAttrType::Func { rename },
// })
// }
// SIGNAL => Some(BoundAttr {
// attr_name: attr_name.clone(),
// index,
// ty: BoundAttrType::Signal(attr.value.clone()),
// }),
// CONSTANT => Some(BoundAttr {
// attr_name: attr_name.clone(),
// index,
// ty: BoundAttrType::Const(attr.value.clone()),
// }),
// _ => None,
// };

// let new_found = if let Some(mut parser) = KvParser::parse(attributes, "func")? {
// // TODO you-win (August 8, 2023): handle default values here as well?

// let rename = parser.handle_expr("rename")?.map(|value| value.to_string());

// parser.finish()?;

// Some(BoundAttr {
// attr_name: attr_name.clone(),
// index,
// ty: BoundAttrType::Func { rename },
// })
// } else if KvParser::parse(attributes, "signal")?.is_some() {
// Some(BoundAttr {
// attr_name: attr_name.clone(),
// index,
// ty: BoundAttrType::Signal(attr.value.clone()),
// })
// } else if KvParser::parse(attributes, "constant")?.is_some() {
// Some(BoundAttr {
// attr_name: attr_name.clone(),
// index,
// ty: BoundAttrType::Const(attr.value.clone()),
// })
// } else {
// None
// };

let new_found = match attr_name {
name if name == "func" => {
// TODO you-win (August 8, 2023): handle default values here as well?

// Safe unwrap since #[func] must be present if we got to this point
let mut parser = KvParser::parse(attributes, "func")?.unwrap();

let rename = if let Ok(Some(value)) = parser.handle_expr("rename") {
Some(value.to_string())
} else {
None
};

Some(BoundAttr {
attr_name: attr_name.clone(),
index,
ty: BoundAttrType::Func { rename },
})
}
name if name == "signal" => {
// TODO once parameters are supported, this should probably be moved to the struct definition
// E.g. a zero-sized type Signal<(i32, String)> with a provided emit(i32, String) method
// This could even be made public (callable on the struct obj itself)
Some(BoundAttr {
attr_name: attr_name.clone(),
index,
ty: BoundAttrType::Signal(attr.value.clone()),
})
const FUNC: &str = "func";
const SIGNAL: &str = "signal";
const CONSTANT: &str = "constant";

let mut name_and_parser = None;

for name in [FUNC, SIGNAL, CONSTANT] {
if let Some(valid_parser) = KvParser::parse(attributes, name)? {
if name_and_parser.is_some() {
bail!(
&error_scope,
"at most one #[func], #[signal], or #[constant] attribute per declaration allowed",
)?;
}
name if name == "constant" => Some(BoundAttr {
attr_name: attr_name.clone(),
index,
ty: BoundAttrType::Const(attr.value.clone()),
}),
_ => None,
};
name_and_parser = Some((name, valid_parser));
}
}

// Validate at most 1 attribute
if found.is_some() && new_found.is_some() {
bail!(
&error_scope,
"at most one #[func], #[signal], or #[constant] attribute per declaration allowed",
)?;
let Some((name, mut parser)) = name_and_parser else {
return Ok(None);
};

let index = parser.index();
let attr_name = util::ident(name);

let attr = match name {
FUNC => {
// TODO you-win (August 8, 2023): handle default values here as well?

let rename = parser.handle_expr("rename")?.map(|value| value.to_string());

Some(BoundAttr {
attr_name,
index,
ty: BoundAttrType::Func { rename },
})
}
SIGNAL => Some(BoundAttr {
attr_name,
index,
ty: BoundAttrType::Signal,
}),
CONSTANT => Some(BoundAttr {
attr_name,
index,
ty: BoundAttrType::Const,
}),
_ => unreachable!(),
};

found = new_found;
}
parser.finish()?;

Ok(found)
Ok(attr)
}

// ----------------------------------------------------------------------------------------------------------------------------------------------
Expand Down
8 changes: 7 additions & 1 deletion godot-macros/src/util/kv_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pub(crate) type KvMap = HashMap<Ident, Option<KvValue>>;
pub(crate) struct KvParser {
map: KvMap,
span: Span,
index: usize,
}

#[allow(dead_code)] // some functions will be used later
Expand All @@ -41,7 +42,7 @@ impl KvParser {
pub fn parse(attributes: &[Attribute], expected: &str) -> ParseResult<Option<Self>> {
let mut found_attr: Option<Self> = None;

for attr in attributes.iter() {
for (index, attr) in attributes.iter().enumerate() {
let path = &attr.path;
if path_is_single(path, expected) {
if found_attr.is_some() {
Expand All @@ -51,6 +52,7 @@ impl KvParser {
let attr_name = expected.to_string();
found_attr = Some(Self {
span: attr.tk_brackets.span,
index,
map: ParserState::parse(attr_name, &attr.value)?,
});
}
Expand All @@ -63,6 +65,10 @@ impl KvParser {
self.span
}

pub fn index(&self) -> usize {
self.index
}

/// - For missing keys, returns `None`.
/// - For a key with no value, returns `Some(None)`.
/// - For a key with a value, returns `Some(value)`.
Expand Down

0 comments on commit 5f70989

Please sign in to comment.