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

Support function receivers self, mut self, &self, &mut self #27

Merged
merged 3 commits into from Jul 11, 2022

Conversation

Bromeon
Copy link
Collaborator

@Bromeon Bromeon commented Jun 26, 2022

Adds support for receiver parameters self, mut self, &self, &mut self.

Also captures several "non-content" tokens like ( ) parameter list group, : parameter name-value separator, -> return type separator.

This PR contains breaking changes, as it splits FunctionParameter up:

#[derive(Clone, Debug)]
pub enum FunctionParameter {
    Receiver(FunctionReceiverParameter),
    Typed(FunctionTypedParameter),
}

// self, mut self, &self, &mut self
#[derive(Clone, Debug)]
pub struct FunctionReceiverParameter {
    pub attributes: Vec<Attribute>,
    pub tk_ref: Option<Punct>,
    pub tk_mut: Option<Ident>,
    pub tk_self: Ident,
}

// name: type
#[derive(Clone, Debug)]
pub struct FunctionTypedParameter {
    pub attributes: Vec<Attribute>,
    pub tk_mut: Option<Ident>,
    pub name: Ident,
    pub tk_colon: Punct,
    pub ty: TyExpr,
}

@PoignardAzur
Copy link
Owner

Always a pleasure to get a high-quality PR =)

From a quick read-through, everything looks good. I'll look at it again and (presumably) merge it before the end of the week.

src/parse.rs Outdated Show resolved Hide resolved
@Bromeon
Copy link
Collaborator Author

Bromeon commented Jun 28, 2022

Thanks a lot for the kind words 😊

Renew insta snapshots for function parsing

Added:
* Function::tk_params_parens -- () around parameter lists
* Function::tk_return_arrow -- -> before return type
* FunctionParameter::tk_colon -- : between parameter name + type
@PoignardAzur
Copy link
Owner

Just a quick note to say I didn't forget about this PR. Still hoping to merge it and the other one soon.

@Bromeon
Copy link
Collaborator Author

Bromeon commented Jul 5, 2022

No worries, and thanks for the heads-up!

One thing I was wondering when working on the other PR: would it make sense to use shorter identifiers, since this got quite long with the "Receiver"/"Typed" addition -- also for consistency with other identifiers like GenericParam? Instead of

pub enum FunctionParameter { ... }
pub struct FunctionReceiverParameter { ... }
pub struct FunctionTypedParameter { ... }

there could be

pub enum FnParam { ... }
pub struct FnReceiverParam { ... }
pub struct FnTypedParam{ ... }

If yes, I could do that either here or in a separate follow-up "consistency PR". Other types might have similar points.

@PoignardAzur
Copy link
Owner

I'd lean towards doing it once both PRs are merged.

@Bromeon
Copy link
Collaborator Author

Bromeon commented Jul 6, 2022

Sounds good to me!

@PoignardAzur
Copy link
Owner

Some feedback:

  • In the codebase, "consume_xxx" is meant to indicate functions that return an option or a vector that is potentially empty. Functions that panic if they can't find tokens they're looking for should be called "parse_xxx". I'm thinking of consume_fn in particular which should be renamed to parse_fn. (I don't know if this matches common practice, but I'd like to stay consistent)
  • You added a use case for functions like fn foobar(mut arg: Foo) (functions with mutable arguments). You should add a unit test for that case (the tests only cover the mut self case).
  • When parsing a function, the parser drops the semicolon if there's one. It should store it in the Function type instead of re-creating one in the ToTokens impl.
  • FWIW, I tried to stick to a flow in the codebase where dependencies go "up". That is, the "exported" functions are at the bottom, and the function they call are above them. It's not better or worse than the alternative, but I'd like to stay consistent there too.

Overall, none of these are blockers, so I'm merging the PR now.

@PoignardAzur PoignardAzur merged commit fcdbb23 into PoignardAzur:main Jul 11, 2022
@Bromeon Bromeon deleted the feature/fn-improvements branch July 11, 2022 12:34
@Bromeon
Copy link
Collaborator Author

Bromeon commented Jul 11, 2022

Thanks a lot for the merge and the feedback!

In the codebase, "consume_xxx" is meant to indicate functions that return an option or a vector that is potentially empty. Functions that panic if they can't find tokens they're looking for should be called "parse_xxx". I'm thinking of consume_fn in particular which should be renamed to parse_fn. (I don't know if this matches common practice, but I'd like to stay consistent)

I see, but this is currently not done consistently -- e.g. consume_attributes() or consume_vis_marker() both panic.


Slightly off-topic, what do you think about translating those panics into Result<..., venial::Error> and propagate them via ? operator, to have errors messages with spans? I'm aware that venial's declared trade-off is "It doesn't attempt to recover gracefully from errors. If [the input isn't a valid declaration], venial will summarily panic.", so I wanted to ask what your stance here is if readability doesn't suffer much.

To clarify, in my own code (that uses venial), I wrote this little utility:

type ParseResult<T> = Result<T, venial::Error>;

pub fn fail<R, T>(msg: &str, tokens: T) -> ParseResult<R>
where
    T: Spanned,
{
    Err(venial::Error::new_at_span(tokens.__span(), msg))
}

Usage:

fn try_parse(...) -> ParseResult<...> {
    // ...
    match tk {
        TokenTree::Ident(ident) => {
            let key = /* extract stuff, etc */
        }
        _ => fail("attribute must start with key", tk)?,
    }
}

Non-panicking functions would also help the problem mentioned here: when encountering const, I could first try consume_fn, on error fallback to consume_constant and only then cause an error.

Edit: after some more thought, it might actually be quite a bit of effort, since it's not just the direct panic! calls, but also assert!, unwrap(), expect(), operator[] etc.


Your other points should be clear, I'll integrate them as a separate commit in the impl PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants