-
-
Notifications
You must be signed in to change notification settings - Fork 189
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
Fix question mark lint. #592
Conversation
API docs are being generated and will be shortly available at: https://godot-rust.github.io/docs/gdext/pr-592 |
Thanks for the cleanup! How exactly did you run local lints? We should find out why they are not triggered in GitHub's CI.
I disagree -- I would avoid Some(self.peek()?.as_ident().is_ok()) , the |
I literally just ran I'm also gonna annotate line 58 of godot_api.rs, the field is used, but never read for the moment. |
What does I'm asking because the clippy invocation in check.sh and in CI should be very close: Lines 130 to 138 in c006a09
gdext/.github/workflows/full-ci.yml Lines 89 to 97 in c006a09
The check.sh one uses |
Oh I just realized I default to nightly. I can override that for the future. |
godot-macros/src/class/godot_api.rs
Outdated
#[allow(dead_code)] | ||
Const(AttributeValue), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only the inner AttributeValue
is unused, there are Const(_)
match arms.
Can you apply the attribute to the nested type?
585f843
to
b601c07
Compare
Also, I think some of the clippy stuff is only running on changed files, which is how GitHub caught the |
Actually, I think the documentation of
Neither documentation reflects this behavior accurately. Hypothetically, changing the latter to this:
would be the more accurate semantics if you never want it to Error, which is what the documentation implies. But it is used as written (returning an error without consuming the next pair.) |
We might not need Or we could just add an Actually it already doesn't need to be an Option. We only care if it's false, so we can just return The |
A lot of this grew historically, in the sense that whenever some new parsing feature was needed, a new method was added without consolidating the existing API. So I can imagine that If you see a straightforward path to improve this, feel free 🙂 also in terms of docs. I'm not yet sure what features we're going to need in the future, so requirements might change again.
Did you mean #484 with export ranges? Regarding |
Btw, I'm currently working on #539 and refactor |
IMO: For the vast majority of our macros we tend to look solely for identifiers, so
To cut |
I've been giving it a go, removed the function that was hacked in purely for that commit, I AM inverting the return types, but since some of the other parsers return straight tokens and we care more about the state of this one, I actually think the semantics might be a little clearer this way. There's a little more indentation in I didn't change anything in Actually, I won't commit it at all until I know whether I should have even been working on these. Only changed Honestly, exposing peek should be fine: |
Ah yeah, the You could add those changes as a separate commit, then it's possible to inspect them in isolation 🙂 |
I did have to expose peek, I realized, so some of it might have been for nothing. THAT SAID, I didn't run into any errors when I was consuming the next identifier unwittingly. So an option was just getting skipped and we aren't testing for that, since all the remaining ones were valid. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the update!
To be honest, I find the new notation more confusing than before.
while let Some(option) = parser.next_any_ident(&ALLOWED_OPTIONS[..])? {
options.insert(option.to_string());
while let Some(res) = parser.next_allowed_ident(&ALLOWED_OPTIONS[..]) {
let option = res?.to_string();
options.insert(option);
or:
while let Some(flag) = parser.next_ident()? {
flags.push(flag)
while let Some(flag_res) = parser.next_ident() {
flags.push(flag_res?)
The inner ?
expresses less clearly that the operation can fail, and is also less idiomatic in the context where each parser function is followed by the ?
. For example, it's now asymmetrical with the entire API of KvParser
.
What was the main rationale? Avoiding the transpose()
? Because overall, I don't see significant readability gains -- some places are slightly more concise, some more verbose.
Honestly most of it was to clean up the list_parser, a lot of the awkward functions were a direct result of In fact, the inversion is very weird here: gdext/godot-macros/src/util/kv_parser.rs Lines 34 to 37 in c006a09
If parse returned an Option<Result> that could be:
Self::parse(attributes, expected)
.unwrap_or(bail!(context, "expected attribute #[{expected}], but not present",)) But instead we have to deal with an Frankly the whole thing could be written as an iterator over |
Sorry, this ended up being WAY more of a rabbit hole than I planned on when I said to myself "hey those docs look weird" |
This is not generally true; I look at it from this perspective: these functions could all panic. The result would be the same, but it's less user-friendly because error message and span aren't propagated nicely. But we would then have signatures like: impl KvParser {
/// is key present
fn handle_alone(&mut self, key: &str) -> bool;
/// expect key's value as ident
fn handle_ident(&mut self, key: &str) -> Option<Ident>;
...
} Now due to ergonomics, we want to propagate the error -- so I wrap it in Result. This is symmetric with the above: impl KvParser {
/// is key present
fn handle_alone(&mut self, key: &str) -> ParseResult<bool>;
/// expect key's value as ident
fn handle_ident(&mut self, key: &str) -> ParseResult<Option<Ident>>;
...
} The return type is always consistent, whether the inner type is
The semantics of
The inversion is a direct consequence of
Could you elaborate? You mean |
I meant in
I think the other way around actually can result in better span, because at the outer level you have more info on what's been tried and what the high-level goal is. You're never getting rid of the error, you're just deferring a decision on how to handle it, so you can use the inner span, or combine it with already parsed tokens to make a better message. I don't find the single-nesting of a
The way I see it, if nothing was found, no parsing was done. Then you can deal separately with whether you WANT something to have been found and if you did, whether it was what you wanted. The higher level functions should know better than the simple ones whether a failure to parse is a problem or not, sometimes it is, and it's easy to propogate the error at that point, other times it just means you have to try to parse the next thing.
I meant that most parsing is basically a batching iterator, which is why I tend to default to returning Now the functions that consume that iterator will indeed probably want to return a
In the case of handle_alone_ident(key).is_some() and an explicit early return with as is it could still be expressed with handle_alone_ident(key).is_ok_and(|opt| opt.is_some()) But I find those semantics messier and less flexible in the general case. similarly, |
That's not true for gdext/godot-macros/src/util/kv_parser.rs Lines 83 to 99 in c006a09
(Btw, I renamed
But that's why there are different methods with different guarantees:
We can of course keep the interface in To give a bit of background, originally we just worked with the So when you say "Then you can deal separately with whether you WANT something to have been found and if you did, whether it was what you wanted." -- this goes against the idea of having a specialized (I'm mostly talking about |
Yeah I think a lot of this does just boil down to API preference. I think if I were to go through all of it with a fine-toothed comb I could get something that was both consistent and intuitive across the board while remaining simple at the higher levels, but I definitely understand the value of a consistent API and it's not worth the duplicated effort and risk of miscommunication when you're already refactoring. I'm very much not opposed to a specialized I did misread I'll revert the transposing changes but still expose |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you! 🙂
For next time, maybe consider making comments a bit longer. Not important enough to block merging though 😉
Along with a warning about unused field here
gdext/godot-macros/src/class/godot_api.rs
Line 59 in c006a09
As for the other, after fixing the
?
lint on the check, I felt that the bindings and extra lines in the changed functions were redundant and didn't really help readability, so I changed both to be what I thought was a little clearer. I thought about including this in my last one but it felt unrelated enough. Sorry if I'm spamming them.I also got a lot of warnings suggesting
addr_of!
instead of a mutable static, but I think that will be covered by #581