Is there an 'upgrade project to new API' tool for Rust?


#1

Hi

Rust is an actively developing language that experiments with new ideas and actively gains new features. API changes, idiomatic syntax structures are changing over time. And it is a good thing to see the language developing.

One of the downside of moving language is that existing codebase need to be adopted for new language structures/API. The changes are usually trivial but still require time from the project developers.

The best way to resolve this situation is to have a command-line tool that allows to do mass refactoring based on some AST conversion rules. Such rules can be as simple as 'rename method ‘String::foo’ to ‘String::bar’ but more complex as changing syntax, adding/removing parameters, function inlining, etc…

Here is an use case: I have a large project and just updated rust compiler requirement from 1.12 to 1.14 and I want to move the project to the new syntax, e.g.:

  • use ‘…’ for matching where appropriate
  • remove ‘empty string’ argument from println!()
  • replace try! macro with ‘?’ operator

Instead of grepping code and manually fixing it I would prefer to run a tool similar to ‘rustfmt’. But instead of formatting the code it manipulates AST and changing it according to some rules. Think it’s like ‘Rails automatic db migration’ but for sources instead of records in a database. Such tool would make compiler upgrades much more pleasant.

And such tool can be made generic to work with any crate API upgrades: e.g. crate ‘foo’ changed its API so the crate developer can add a migration rule that describes code that use to be before and how it should be transformed. Such rule can be put to the project sources and distributed with Cargo.

I was looking for such Rust tool and did not find it. Do you think such tool would be useful? Worth filing a request to Rust team?


#2

Hi! InteliJ-Rust is perfectly capable of such AST-level transformations. For example, here’s the code to replace try! with ?: https://github.com/intellij-rust/intellij-rust/blob/master/src/main/kotlin/org/rust/ide/inspections/RustTryMacroInspection.kt.

It’s not easy to define user-defined transformations at the moment (but I am almost that this is possible although I’ve not done it myself). But you can always just fork plugin and write your custom refactoring (I’ve done it a couple of times). And feature requests are welcome!

I think I’ll implement println!("") -> println!() inspection right now :slight_smile:


#3

In non-Intellij world such features should be provided by the RLS I think: https://github.com/jonathandturner/rls

But it’s not released yet and don’t have necessary infrastructure for implementing such features at the moment.


#4

Worth noting the Go language has “go fix” (https://blog.golang.org/introducing-gofix) which is impressively flexible.


#5

@Ixrec, exactly like this! It would be great to have a tool like this for Rust. But it looks like gofix is for Go language changes only. I think it would be beneficial if any developer can add ‘fixes/migrations’ for its own library.


#6

It can do library changes too:

You can extend gofix to support changes to your own APIs. The gofix program is a simple driver around plugins called fixes that each handle a particular API change. Right now, writing a new fix requires doing some scanning and rewriting of the go/ast syntax tree, usually in proportion to how complex the API changes are.

As far as I know, the main things that do potentially limit go fix’s relevance to Rust is not having a broadly enforced official coding style or standard libraries for parsing and printing Rust ASTs while preserving that coding style:

Gofix is possible because Go has support in its standard libraries for parsing Go source files into syntax trees and also for printing those syntax trees back to Go source code. Importantly, the Go printing library prints a program in the official format (typically enforced via the gofmt tool), allowing gofix to make mechanical changes to Go programs without causing spurious formatting changes. In fact, one of the key motivations for creating gofmt—perhaps second only to avoiding debates about where a particular brace belongs—was to simplify the creation of tools that rewrite Go programs, as gofix does.

My impression is that Rust is somewhat less opinionated than Go, so we probably want to look into whether it’s feasible for “rust fix plugins” to support ergonomic code-editing-code that makes no spurious format changes by default no matter what coding style is in use.


#7

Here’s rustfix, though I don’t know how similar it is to gofix.

A tool like you describe would be quite welcome.