Pre-RFC - Rename annotations

Discussion on r/rust:

https://www.reddit.com/r/rust/comments/1i88ugg/prerfc_rename_annotations/

7 Likes

This seems like a pretty good idea to me. It also seems or have a reasonably small scope. As a user of rust o would like this.

I don't know how difficult this would be to implement though.

We already have this under #[rustc_confusables] and #[doc(alias = "OldName")] (but this later one works in fewer places, and we should document and fix that), but I think it currently only works for associated items. There's already a ticket to extend support to more items. I'm on board with the suggestion, even if I have some qualms about the specifics (the name, having a way to provide a message to say "this was renamed in version X", etc.).

Carrying over my reddit comment

In my ideal world, we'd have sufficient attributes so that call graph analysis could report what changed for how you use a library, offering a more specialized view than a changelog.

We already have

  • Addition (unstable as #[stable])
  • Deprecation

We would also need

  • Removal (more general than a move)
  • Behavior change

Having a more general way of communicating removals would cover this case as well as others.

Either a #![diagnostic::removed("name")] within the module where this happened or an annotation on an item that is meant not to be used (like #[deprecated] but instead of a lint a hard error) would help there, but you still need a mechanism to deal with moves. I'm not sure how we could deal with behavior changes, other than maybe something that hooks with the versioning system... That seems to be straying into semver-checks territory.

1 Like

@ekuber and I talked about this at Rust Nation. If you remove the function we have #[deprecated]. If you move the function a proposal like this blog post suggests works really well. But what do you do if your change does not fit one of these pre-paved paths? The case we were discussing was taking a function with a Boolean argument and turning it into a function that takes a dedicated enum. Or even into two functions, taking foo(..., true) to true_foo(...) and foo(..., false) to false_foo(...).

This was a quick conversation at the end of a long conference, so I don't know if what we came up with was a good idea, But I still like it. #![diagnostic::deprecated_with_macro_suggestion(name_of_macro)] gets put on the old deprecated function. The name_of_macro gets past the call site where the old function is used, and if it matches the output of the function is suggested as part of the compiler output.

2 Likes

Thanks for that reminder. I'd forgotten about the idea of leveraging macros by examples as a mechanism for user supplied code transformations that the compiler could apply. I still think that it'd be a neat way of doing that.

1 Like

I feel like this is begging for a complimentary pair in the form of #[deprecated(alternative = crate::new::Foo)].

1 Like

These more complex use cases are real and we'd want syntax to cover them, but an advantage of having a dedicated syntax for the "item was moved from here to there but kept the same signature" case is that you can get automated fixes for it, through cargo fix or code actions or whatever else, with minimal user input.

I guess it could be a special case of your macro syntax.

3 Likes

I absolutely agree we should have dedicated syntax for the simple case. We should fight the temptation to have scope creep, for the simple syntax to be able to handle ever more complicated cases. If we have a general solution at least scoped out then it becomes much easier to say "this syntax only covers this simple use case if you want something more complicated use the general solution".

1 Like

While the macro idea could be quite great in expressing complex changes, having an upfront annotation for these still somewhat typical ones seems useful to me. E.g., the old type signature could perhaps be signaled thus:

#[diagnostic::refactored(from = pub fn foo(circles_instead_of_squares: bool) -> i32]
pub fn foo(shape: CircleOrSquare) -> i32

I've wanted to use #[deprecated] on use statements in the past to handle renames, but unfortunately deprecated isn't supported in this situation:

pub mod old_path {
   #[deprecated(note = "renamed to new_item_name")] // doesn't work ;(
   pub use new_path::new_item_name;
}