Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

add function_export_name_changed lint #728

Merged
merged 5 commits into from
Apr 1, 2024

Conversation

suaviloquence
Copy link
Contributor

@suaviloquence suaviloquence commented Mar 30, 2024

Second bullet point of #502. Should I put all the lints in one (draft, I haven't finished them yet) PR or do separate PRs?

@suaviloquence
Copy link
Contributor Author

FWIW here's the line that breaks nightly tests - it doesn't seem like an issue with this lint.

@obi1kenobi
Copy link
Owner

Separate PRs for each lint is fine. That's usually faster and easier to review, and shows up more nicely in the changelog as well.

That test has been flaky for a while and I'm not sure why. I've never managed to reproduce the flakiness locally, so my gut feeling is that it might possibly be a GitHub Actions bug of some sort? Not sure, haven't had time to dig into it... Don't worry about it, I don't think you broke any of it — I just restarted it and I expect it'll probably pass.

Copy link
Owner

@obi1kenobi obi1kenobi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I might have unintentionally pointed you a bit in the wrong direction on this. Sorry about that!

It should be easy to straighten things out. This is part of why semver linting is so hard! Too many edge cases! But getting them right is important, and I feel like our users will appreciate it.

src/lints/function_export_name_changed.ron Outdated Show resolved Hide resolved
... on Function {
name @output

export_name @filter(op: "!=", value: ["%export_name"]) @filter(op: "!=", value: ["$null"])
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a failure of our docs, so don't worry about it -- there's a built-in filter operator for this:

Suggested change
export_name @filter(op: "!=", value: ["%export_name"]) @filter(op: "!=", value: ["$null"])
export_name @filter(op: "!=", value: ["%export_name"]) @filter(op: "is_not_null")

}"#,
arguments: {
"zero": 0,
"null": null,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"null": null,

since we used the built-in filter operator for this

Comment on lines 40 to 44
item @fold @transform(op: "count") @filter(op: "=", value: ["$zero"]) {
... on Function {
export_name @filter(op: "=", value: ["%export_name"])
}
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be a good idea to write the lint for "export name no longer exists" first, just to get us a bit more test coverage and help us decide how to split the responsibilities between this lint and that one.

Here's what I'm worried about.

If both this lint and that other one enforce that the export name doesn't exist anymore, then it sounds like this lint is just a stricter version of that other one -- so whenever this one triggers, the other one will too. We want to avoid that.

So maybe we reshape this lint a bit to say "a function in the public API has changed its export name in the ABI" instead. So the invariant that is broken (and that we're reporting) is "call func() via the API, and export_func() via the ABI, those do the same thing because they are the same function." If the ABI export name changes to something else, then even if the ABI name export_func() continues to exist on some other function, this is likely worth flagging because it's a possible bug.

If we do end up taking that route, then I think:

  • This block I've highlighted becomes unnecessary.
  • The other lint doesn't need to look at importable_path at all, and can just focus on export_name alone.

What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I was thinking about that when I was writing it - it was pretty much the same query except checking if the current export_name is null. If we don't need granular feedback between missing vs changed, they can be easily combined. Do you think it's necessary?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not at my computer right now but I can push the other lint to a branch when I get a chance

Comment on lines 29 to 37
/// negative test - export name on one function gets moved to the other
/// this is not necessarily a breaking change, as long as the ABIs are the same
/// but that is a different lint
pub mod export_name_moved {
pub fn export_name_moved_1() {}

#[export_name = "export_name_moved"]
pub fn export_name_moved_2() {}
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we may want to flag this, because a public API call to export_name_moved::export_name_moved_1() is no longer equivalent to a call to export_name_moved() via the ABI.

See comment on the lint above for details.

@obi1kenobi
Copy link
Owner

obi1kenobi commented Mar 30, 2024 via email

@suaviloquence
Copy link
Contributor Author

All good. For these categories from the original issue:

  • function with the same export name takes different number of arguments
  • function with the same export name has a parameter whose type changes to one with a different ABI
    it doesn't matter what the type is called, just whether it has the same ABI or not
  • function with the same export name changes what it returns (starts / stops returning data, or changes to return type with different ABI)
    this can be multiple lints; e.g. separate lints for starts / stops / changes return type ABI

Would these (just) apply to exported non-public functions? Because changing the API of a public function would already be a breaking change, so do we want to flag this twice?

@obi1kenobi
Copy link
Owner

That's a great question.

I think it's reasonable to start by flagging twice here, because we're flagging two separate problems: the API is broken, and the ABI is also broken.

Down the line, if this proves to be problematic for any reason, we can build something more sophisticated. For example, we could have three lints instead of two: broken API but not ABI, broken ABI but not API, and both broken at once. But going straight for this seems like major overkill right now, so let's go with the simpler option of flagging twice for now.

@suaviloquence
Copy link
Contributor Author

OK, I consolidated the two cases

Copy link
Owner

@obi1kenobi obi1kenobi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a set of tiny nitpicks. The lint is correct and the tests are in great shape as well. Less than 5min of polishing left and this is good to merge.

Comment on lines 5 to 6
required_update: Major, // Not specified in rust semver doc? most similar is for #[repr] changes: https://doc.rust-lang.org/cargo/reference/semver.html#type-layout
reference_link: Some("https://doc.rust-lang.org/reference/abi.html#the-no_mangle-attribute"), // TODO
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you mean to leave both of these comments in the file that will get merged?

Asking because the TODO seems completed, and the other comment seemed like a question for reviewers that I believe I replied to in my previous comment near here.

src/lints/function_export_name_changed.ron Outdated Show resolved Hide resolved
src/lints/function_export_name_changed.ron Outdated Show resolved Hide resolved
test_crates/function_export_name_changed/new/src/lib.rs Outdated Show resolved Hide resolved
test_crates/function_export_name_changed/old/src/lib.rs Outdated Show resolved Hide resolved
Comment on lines +33 to +35
/// positive test - export name on one function gets moved to the other
/// this is still a breaking change because a call to `export_name_moved_1` in the API
/// is no longer equivalent to a call to `export_name_moved` in the ABI
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great comments throughout, and this one is particularly good! Well done!

src/lints/function_export_name_changed.ron Outdated Show resolved Hide resolved
suaviloquence and others added 2 commits March 31, 2024 17:18
Co-authored-by: Predrag Gruevski <2348618+obi1kenobi@users.noreply.github.com>
- remove old comments
- add output from new `new_export_name` field
@suaviloquence
Copy link
Contributor Author

Thanks for the feedback!

Copy link
Owner

@obi1kenobi obi1kenobi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome job!

@obi1kenobi obi1kenobi merged commit 1a48764 into obi1kenobi:main Apr 1, 2024
33 checks passed
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants