Open ytmimi opened 1 year ago
cc: @rust-lang/style
Would love some feedback on this to make sure that my ideas for how to implement style_edition
line up with your expectations for the design laid out in the RFC.
Most of this seems fine to me.
One thing that would be good to document explicitly: is it OK for some formatting to just directly look at the style edition, rather than requiring a configuration option for which the style edition sets a default? I'm asking because, for instance, backwards-incompatible bugfixes might not be worth a dedicated configuration option (especially not a stable one), and should perhaps just condition on the style edition directly. Or are you proposing that even those should be keying off of a (perhaps always unstable) "bugfix_2024_xyz" config option that defaults to true in 2024+ and false in 2015/2018/2021?
Stating explicitly: that's a rustfmt team call, not a style team call. My only interest in that is making sure that adding changes to a style edition has minimal overhead, and that we never find ourselves going "that change isn't worth it because there's too much overhead in adding a style edition change".
Also, I'm not sure I followed what this was intended to capture:
Not a direct requirement for style_edition, but I think all configurations must define backwards compatible defaults.
One thing that would be good to document explicitly: is it OK for some formatting to just directly look at the style edition, rather than requiring a configuration option for which the style edition sets a default?
Yes that's totally fine, and I was expecting things to work like that. We currently use Version::Two
to gate bug fixes throughout the codebase (See https://github.com/rust-lang/rustfmt/issues/5577 for examples) and I'd expect style_edition
to eventually replace version
in that regard.
Also, I'm not sure I followed what this was intended to capture:
Not a direct requirement for style_edition, but I think all configurations must define backwards compatible defaults.
It's hard to say exactly what I was thinking since it's been some time since I wrote this up. It's possible I didn't articulated myself very well. At the end of my proposal above I describe two mechanisms we could implement to allow and disallow some configuration options from being set for a given style edition. I think users might find it odd if default configurations from one edition weren't available in newer editions and vice versa, but maybe this isn't an issue we need to worry about?
@ytmimi
This means that the
style_edition
specified on the command line has the highest priority, followed by explicitly settingstyle_edition
inrustfmt.toml
followed by edition specified on the command line and finally edition listed inrustfmt.toml
. If none of the values are provided we'll fall back to the default edition which I believe is 2015.
Is the last rustfmt.toml
(emphasis mine) supposed to be Cargo.toml
?
It's bee a very long time since I've looked at this, but I'm pretty sure I meant rustfmt.toml
since rustfmt doesn't use Cargo.toml
for any of its configurations. That being said, cargo fmt
will look up the edition
listed in the Cargo.toml
file and pass that along to rustfmt (not sure if you were aware of that), and you can run cargo fmt -v
to see the underlying rustfmt invokation and what arguments cargo fmt
passes along.
Background
RFC "Style Evolution" (https://github.com/rust-lang/rfcs/pull/3338). Upstream tracking issue: https://github.com/rust-lang/rust/issues/105336
Requirements For The New
style_edition
configAfter reading through the RFC these are the key requirements I found:
style_edition
2015, 2018, or 2021 will use the existing default configuration valuesstyle_edition
(Rust 2024 and onwards) may use new default configuration values.style_edition
will use the same value as was configured foredition
unless--style-edition
is explicitly set when invoking rustfmt e.g.rustfmt --style-edition 2024
or specifically configured inrustfmt.toml
. The precedence should be defined as:(CLI) --style-edition
>(TOML) style_edition
>(CLI) --edition
>(TOML) edition
style_edition
specified on the command line has the highest priority, followed by explicitly settingstyle_edition
inrustfmt.toml
followed byedition
specified on the command line and finallyedition
listed inrustfmt.toml
. If none of the values are provided we'll fall back to the default edition which I believe is2015
.style_edition
, but I think all configurations must define backwards compatible defaults.style_edition
values will be initially introduced as unstable, which don't provide any stability guarantees.Current Configuration Design
All types used for configuration must implement the
ConfigType
trait. There's a handy#[config_type]
attribute macro which helps us easily implementConfigType
for enums.Currently, the
Config
struct and all its fields are set using thecreate_config!
macro. The macro lets us easily define the name, the type (which implementsConfigType
), the default value, and the stability of the rustfmt configuration as well as giving it a description.The current system only allows setting a single default value for each configuration option.
We can acquire a
Config
by calling one the following:Config::default()
to return the default configConfig.fill_from_parsed_config
to mutate the current config with values parsed from arustfmt.toml
New Design With
style_edition
The
Edition
enum is a stable configuration option. we can use the#[unstable_variant]
attribute to markEdition::Edition2024
as unstable. However, if changing the stability of an already stable variant is an unacceptable breaking change then I'd propose adding a newStyleEdition
enum that maintains parity withEdition
and markingStyleEdition::Edition2024
as an#[unstable_variant]
. Oncestyle-edtion 2024
is stabilized we could remove theStyleEdition
enum in favor ofEdition
.Add a new
StyleEditionDefault
trait that will determine the default value for a given rustfmt configuration based on the edition and a newError
type to enumerate all the errors that could occur when trying to construct aConfig
, e.g the config not being available for the given style edition. Here's the general idea: (Note bounds may need to be revised)Instead of declaring default values directly in the
create_config!
macro we'll define new unit structs for existing non-enum configurations and implementStyleEditionDefault
for those new types. Configuration values using enums will implementStyleEditionDefault
directly.For example, the
max_width
config, which is stored as ausize
could be implemented as:and a config like
NewlineStyle
which is defined as an enum could be implement as:Once we've implemented
StyleEditionDefault
for all exiting configuration values the call tocreate_config!
could be modified as follows:A new constructor for
Config
could be addedWe'll remove
Config::default()
in favor of adefault
method that returnsResult<Config, ConfigError>
and modifyfill_from_parsed_config
to also returnResult<Config, ConfigError>
.Additional changes regarding error handling / error propagation will need to be made to support the config constructors now being fallible.
Ergonomics
Similar to the
#[config_type]
attribute macro, I propose we also add a#[style_edition]
attribute macro to make it easy to configure default values for all style editions.macro_rules!
macros can additionally help to define the necessary unit structs for configurations using primitive data types.The
#[style_edition]
could work as follows:Setting a default for all style editions
Setting a default value and specific style edition values
Alternative for setting default values for enum configurations
Specifying values that won't be supported by newer style editions
Specify values that won't be supported for older style editions
Using a hypothetical new option to allow rustfmt to continue formatting even when comments would be dropped