rust-lang / rust-lang/rustfmt

Formatting for calls with multiline string arguments

Open
#2,876 8 comments 11 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

A-macros I-poor-formatting P-medium
Dominant language
Rust
Stars
7k
Forks
1.1k
Avg merge
2d 13h
Merged PRs (30d)
24

Description

Currently code passing a multiline string argument to a function will be reformatted to a multiline call no matter whether it needs it or not. Example using unindent crate:

fn main() {
    assert_eq!(code, unindent(r#"
        def hello():
            print("Hello, world!")
        
        hello()
    "#));
}

->

fn main() {
    assert_eq!(
        code,
        unindent(
            r#"
        def hello():
            print("Hello, world!")
        
        hello()
    "#
        )
    );
}

You can see how it becomes hard to track the beginning / end of the string literal and surrounding call due to extra line breaks with varying indentation.

Same happens when using an indoc! macro instead:

fn main() {
    assert_eq!(code, indoc!(r#"
        def hello():
            print("Hello, world!")
        
        hello()
    "#));
}

->

fn main() {
    assert_eq!(
        code,
        indoc!(
            r#"
        def hello():
            print("Hello, world!")
        
        hello()
    "#
        )
    );
}

Reformatting that takes place in these examples kind of defeats the purpose of using indoc! in the first place which is to prettify multiline string literals in code while preserving indentation.

I think it would be possible to special-case indoc! as Rustfmt already does for few other well-known macros, but maybe instead it would be better to fix this issue in general? For example, I like what happens to "multiline" struct literals in same argument position much more:

fn main() {
    assert_eq!(s, wrap(A {
        x: 10,
        y: 20,
        z: 30,
    }));
}

->

fn main() {
    assert_eq!(
        s,
        wrap(A {
            x: 10,
            y: 20,
            z: 30,
        })
    );
}

I understand that their handling is different, as structs can be also reformatted to fit on same line, but still I wonder if it would be possible / make sense to apply same logic to strings and keep beginning and end quotes near their corresponding parentheses of the surrounding call?

cc @dtolnay as author of mentioned crates

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reproducing the unindent and indoc! examples with rustfmt, then trace the formatting logic for function calls containing multiline string arguments. Compare that behavior with the multiline struct-literal example; done means the call formatting keeps the string delimiters and surrounding parentheses readable without unnecessary nesting or line breaks.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
tooling
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.