-
Notifications
You must be signed in to change notification settings - Fork 888
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
Gives up on chains if any line is too long. #3863
Comments
Yeah, AFAICT that's because rustfmt is expecting to be able to rewrite each of the individual ChainItems to be able to format the Chain. When it attempts to rewrite that long string literal in the Chain it fails due to the length which results in the entire original chain being left as-is. Lines 419 to 444 in a15e97f
I think I see a way we could support still wrapping chain elements like your expected output, though it would mean rustfmt emitting a line that exceeds the max_width (although the original line does too, and the wrapped version is more human friendly IMO even with the one longer line). @topecongiro @scampi - any concerns with that? if not, then I'll take a look at implementing something for consideration. |
These do not wrap due to rust-lang/rustfmt#3863.
@calebcartwright sounds good to me! |
I think this issue is duplicate of #2970 . |
Using format_strings pretty much fixed it for me as strings get broken up and |
I just ran into this as well. My code: fn main() {
clap::App::new("foo")
.subcommand(
SubCommand::with_name("bar")
.about("some text here")
.setting(AppSettings::ArgRequiredElseHelp)
.arg(
Arg::from_usage("-l, --latest 'some very long help text goes here [default: default version]'")
.conflicts_with( "template_version" )
)
)
.get_matches();
} rustfmt seems to completely give up after the line with the long help text, and doesn't even try to reindent the I'd expect rustfmt to do best-effort on the long line, and then continue afterward. |
Doing something about this is still on my to-do list, though I do think it's worth expanding a bit on what folks have in mind when we say "best effort". It seems this is most often encountered with a single long string, but will also note there are plenty of other scenarios that can run into this where IMO the expected behavior starts to get a little tricky. Consider for example a chain whose parent starts in a heavily indented position, and the chain element that exceeds the max width value is a closure param that itself has a sizeable body with additional nested indentation. Would users still want rustfmt to format that closure anyway even though it blows way past the max width value? Are we changing the meaning/honoring of max width with a caveat of "except within chains"? Should rustfmt do this by default or should users have to explicitly opt-in to allowing rustfmt to exceed the max width when there are chains involved? |
@calebcartwright In general, I'd expect rustfmt to indent everything to the correct level, and try to wrap appropriately at line-width, but whether it's successful or not, it should continue on to wrap the rest, yes. It's not "except within chains", it's "when possible, without violating more important constraints like properly indenting". If you have an indent-width of 4, and (say) 15 levels of indentation and a 50-character function name, you cannot format that without exceeding 100 characters, and that shouldn't stop you from continuing on to format more of the line. And if you need to wrap the function parameters, those should still get indented 16 levels, even if one of them is a long string. |
What defines the relative importance of one constraint vs. another though? Is there a consensus on which constraints can be violated? Does the style guide have a prescription? The only thing I'm familiar with (and I know you're far more familiar with the style guide than I @joshtriplett 😄) is https://github.com/rust-dev-tools/fmt-rfcs/blob/7416e12356a55aae959e9629b58eb7c7c3c86f6f/guide/guide.md#indentation-and-line-width It's not that we can't technically make this change, but in these types of scenarios where rustfmt can't satisfy all the constraints it bails and defers to the programmer to format, or refactor, as needed. This is usually quite manageable, and often accompanied with advice like
Precisely and agreed, I was just trying to keep the example in the context of chains given the issue. There's also portions of the user base that do want to strictly enforce the width limits in their code and would leverage options like I also think that if max width were to be changed to more of a soft constraint then we'd need to explicitly convey that rustfmt will format your code within the limit, unless it really can't, in which case it will format out indentations indefinitely as needed. |
I very much agree with Josh here, but I think even bigger issue is that there are no visible errors/warnings that formatting is not possible and why. |
@nazar-pc try running with example output with that enabled:
|
Another bug caused by this issue, that makes my editor's formatter, which uses Input: fn main() {
(
foo,
"this is a long line that breaks rustfmt, let's make it even longer foo bar foo bar foo bar"
)
} Note the trailing space after Output: error[internal]: left behind trailing whitespace
--> /playground/src/main.rs:3:3:12
|
3 | foo,
| ^
|
warning: rustfmt has failed to format. See previous 1 errors. |
@MonliH - that's not really a separate bug or strictly related to this issue. Your original snippet has a trailing space which is the root cause (rustfmt isn't inserting a random trailing space), rustfmt is just leaving your original snippet as is and highlighting the fact that it did not remove the trailing spaces. |
You do not need strings to get this bug: // this does not format
let offenders = current_validators
.into_iter()
.enumerate()
.filter_map(|(_, id)|
<<Runtime as pallet_im_online::Config>::ValidatorSet as ValidatorSetWithIdentification<
sp_runtime::AccountId32>>::IdentificationOf::convert(id.clone()).map(|full_id| (id, full_id)))
.collect::<Vec<IdentificationTuple<Runtime>>>(); // this type alias prevents the formatting bug
type XXX =
<<Runtime as pallet_im_online::Config>::ValidatorSet as ValidatorSetWithIdentification<
sp_runtime::AccountId32,
>>::IdentificationOf;
// this does format
let offenders = current_validators
.into_iter()
.enumerate()
.filter_map(|(_, id)| XXX::convert(id.clone()).map(|full_id| (id, full_id)))
.collect::<Vec<IdentificationTuple<Runtime>>>(); And the giving up propagates to the whole block. |
I would like an option to have |
How has this bug been open for almost 5 years without a fix? |
Why don't you fix it yourself? 🤔 |
i don't have the required programming experience and the rust skills yet |
So be respectful when you speak. Don't act like a self-entitled git (in the traditional sense of the word). |
Another example where a closure is part of the chain: fn main() {
let f = foo("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")
.bar(|| {
foo( 233545 );
if let Some ( x) = foo(3) {
}
});
} |
Turns out rustfmt just givesup on formatting if it can't ensure the code width is less than `max_width`. see: rust-lang/rustfmt#3863
Do I understand correctly that if I have too long string literal then rustfmt will just ignore the whole function without any warning / error, and there is nothing that stable toolchain users can do about it because format_strings and https://rust-lang.github.io/rustfmt/?version=master&search=overflow#error_on_line_overflow are all unstable? And this situation is going on for 5 years now? Great... |
Here's my suggestion: Rustfmt should treat this case the same as Prettier, Biome, Black, Ruff, and I assume many other auto-formatters which I'm not familiar with. Namely, treat the max width as a target but not a strict maximum. They try their best to get lines to fit under that length, but if they can't, they can't and that's fine. They still get the line as short as possible. |
It could write a warning to stderr, with a config option disable_warning_on_ignored_line. And overall I don't understand why it ignores the other lines of code block following the long line. |
I tried using the following rustfmt.toml and nightly rust and it still doesn't seem to be formatting any of my code... I guess this still needs to be fixed in nightly rust as well.
|
If there is a method call chain, and just one call exceeds max_width, it seems like rustfmt gives up and doesn't format anything.
Example:
No format changes will be applied to this. I'm also a bit surprised that
error_on_line_overflow
does not complain.I would expect it to wrap each call chain element, even if one of them is too long, such as:
All default settings.
rustfmt 1.4.8-nightly (afb1ee1 2019-09-08)
The text was updated successfully, but these errors were encountered: