Skip to content
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

Suggest replacing . with :: in more error diagnostics. #136344

Merged
merged 4 commits into from
Feb 20, 2025

Conversation

zachs18
Copy link
Contributor

@zachs18 zachs18 commented Jan 31, 2025

First commit makes the existing "help: use the path separator to refer to an item" also work when the base is a type alias, not just a trait/module/struct.

The existing unconditional DefKind::Mod | DefKind::Trait match arm is changed to a conditional DefKind::Mod | DefKind::Trait | DefKind::TyAlias arm that only matches if the path_sep suggestion-adding closure succeeds, so as not to stop the later DefKind::TyAlias-specific suggestions if the path-sep suggestion does not apply. This shouldn't change behavior for Mod or Trait (due to the default arm's return false etc).

This commit also updates tests/ui/resolve/issue-22692.rs to reflect this, and also renames it to something more meaningful.

This commit also makes the bad_struct_syntax_suggestion closure take err as a parameter instead of capturing it, since otherwise caused borrowing errors due to the change to using path_sep in a pattern guard.

Type alias diagnostic example
type S = String;

fn main() {
    let _ = S.new;
}
 error[E0423]: expected value, found type alias `S`
  --> diag7.rs:4:13
   |
 4 |     let _ = S.new;
   |             ^
   |
-  = note: can't use a type alias as a constructor
+  help: use the path separator to refer to an item
+  |
+4 |     let _ = S::new;
+  |              ~~

Second commit adds some cases for enums, where if there is a field/method expression where the field/method has the name of a unit/tuple variant, we assume the user intended to create that variant1 and suggest replacing the . from the field/method suggestion with a :: path separator. If no such variant is found (or if the error is not a field/method expression), we give the existing suggestion that suggests adding ::TupleVariant(/* fields */) after the enum.

Enum diagnostic example
enum Foo {
    A(u32),
    B,
    C { x: u32 },
}

fn main() {
    let _ = Foo.A(42); // changed
    let _ = Foo.B;     // changed
    let _ = Foo.D(42); // no change
    let _ = Foo.D;     // no change
    let _ = Foo(42);   // no change
}
 error[E0423]: expected value, found enum `Foo`
  --> diag8.rs:8:13
   |
 8 |     let _ = Foo.A(42); // changed
   |             ^^^
   |
 note: the enum is defined here
  --> diag8.rs:1:1
   |
 1 | / enum Foo {
 2 | |     A(u32),
 3 | |     B,
 4 | |     C { x: u32 },
 5 | | }
   | |_^
-help: you might have meant to use the following enum variant
-  |
-8 |     let _ = Foo::B.A(42); // changed
-  |             ~~~~~~
-help: alternatively, the following enum variant is available
+help: use the path separator to refer to a variant
   |
-8 |     let _ = (Foo::A(/* fields */)).A(42); // changed
-  |             ~~~~~~~~~~~~~~~~~~~~~~
+8 |     let _ = Foo::A(42); // changed
+  |                ~~
 
 error[E0423]: expected value, found enum `Foo`
  --> diag8.rs:9:13
   |
 9 |     let _ = Foo.B;     // changed
   |             ^^^
   |
 note: the enum is defined here
  --> diag8.rs:1:1
   |
 1 | / enum Foo {
 2 | |     A(u32),
 3 | |     B,
 4 | |     C { x: u32 },
 5 | | }
   | |_^
-help: you might have meant to use the following enum variant
-  |
-9 |     let _ = Foo::B.B;     // changed
-  |             ~~~~~~
-help: alternatively, the following enum variant is available
+help: use the path separator to refer to a variant
   |
-9 |     let _ = (Foo::A(/* fields */)).B;     // changed
-  |             ~~~~~~~~~~~~~~~~~~~~~~
+9 |     let _ = Foo::B;     // changed
+  |                ~~
 
 error[E0423]: expected value, found enum `Foo`
   --> diag8.rs:10:13
    |
 10 |     let _ = Foo.D(42); // no change
    |             ^^^
    |
 note: the enum is defined here
   --> diag8.rs:1:1
    |
 1  | / enum Foo {
 2  | |     A(u32),
 3  | |     B,
 4  | |     C { x: u32 },
 5  | | }
    | |_^
 help: you might have meant to use the following enum variant
    |
 10 |     let _ = Foo::B.D(42); // no change
    |             ~~~~~~
 help: alternatively, the following enum variant is available
    |
 10 |     let _ = (Foo::A(/* fields */)).D(42); // no change
    |             ~~~~~~~~~~~~~~~~~~~~~~
 
 error[E0423]: expected value, found enum `Foo`
   --> diag8.rs:11:13
    |
 11 |     let _ = Foo.D;     // no change
    |             ^^^
    |
 note: the enum is defined here
   --> diag8.rs:1:1
    |
 1  | / enum Foo {
 2  | |     A(u32),
 3  | |     B,
 4  | |     C { x: u32 },
 5  | | }
    | |_^
 help: you might have meant to use the following enum variant
    |
 11 |     let _ = Foo::B.D;     // no change
    |             ~~~~~~
 help: alternatively, the following enum variant is available
    |
 11 |     let _ = (Foo::A(/* fields */)).D;     // no change
    |             ~~~~~~~~~~~~~~~~~~~~~~
 
 error[E0423]: expected function, tuple struct or tuple variant, found enum `Foo`
   --> diag8.rs:12:13
    |
 12 |     let _ = Foo(42);   // no change
    |             ^^^ help: try to construct one of the enum's variants: `Foo::A`
    |
    = help: you might have meant to construct the enum's non-tuple variant
 note: the enum is defined here
   --> diag8.rs:1:1
    |
 1  | / enum Foo {
 2  | |     A(u32),
 3  | |     B,
 4  | |     C { x: u32 },
 5  | | }
    | |_^
 
 error: aborting due to 5 previous errors

Footnotes

  1. or if it's a field expression and a tuple variant, that they meant to refer the variant constructor.

@rustbot
Copy link
Collaborator

rustbot commented Jan 31, 2025

r? @davidtwco

rustbot has assigned @davidtwco.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

@rustbot rustbot added A-tidy Area: The tidy tool S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-bootstrap Relevant to the bootstrap subteam: Rust's build system (x.py and src/bootstrap) T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Jan 31, 2025
@zachs18
Copy link
Contributor Author

zachs18 commented Jan 31, 2025

If the enum diagnostics aren't worth it I'm fine dropping them from the PR, the type alias one is my main reason for the PR.

@zachs18 zachs18 force-pushed the dot_notation_more_defkinds_3 branch from f2f20f9 to c02efb8 Compare February 1, 2025 00:38
@bors

This comment was marked as resolved.

@zachs18 zachs18 force-pushed the dot_notation_more_defkinds_3 branch from c02efb8 to 4b94807 Compare February 11, 2025 18:53
@davidtwco
Copy link
Member

@bors r+

@bors
Copy link
Contributor

bors commented Feb 18, 2025

📌 Commit 4b94807 has been approved by davidtwco

It is now in the queue for this repository.

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Feb 18, 2025
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this pull request Feb 18, 2025
…_3, r=davidtwco

Suggest replacing `.` with `::` in more error diagnostics.

First commit makes the existing "help: use the path separator to refer to an item" also work when the base is a type alias, not just a trait/module/struct.

The existing unconditional `DefKind::Mod | DefKind::Trait` match arm is changed to a conditional `DefKind::Mod | DefKind::Trait | DefKind::TyAlias` arm that only matches if the `path_sep` suggestion-adding closure succeeds, so as not to stop the later `DefKind::TyAlias`-specific suggestions if the path-sep suggestion does not apply. This shouldn't change behavior for `Mod` or `Trait` (due to the default arm's `return false` etc).

This commit also updates `tests/ui/resolve/issue-22692.rs` to reflect this, and also renames it to something more meaningful.

This commit also makes the `bad_struct_syntax_suggestion` closure take `err` as a parameter instead of capturing it, since otherwise caused borrowing errors due to the change to using `path_sep` in a pattern guard.

<details> <summary> Type alias diagnostic example </summary>

```rust
type S = String;

fn main() {
    let _ = S.new;
}
```

```diff
 error[E0423]: expected value, found type alias `S`
  --> diag7.rs:4:13
   |
 4 |     let _ = S.new;
   |             ^
   |
-  = note: can't use a type alias as a constructor
+  help: use the path separator to refer to an item
+  |
+4 |     let _ = S::new;
+  |              ~~
```

</details>

Second commit adds some cases for `enum`s, where if there is a field/method expression where the field/method has the name of a unit/tuple variant, we assume the user intended to create that variant[^1] and suggest replacing the `.` from the field/method suggestion with a `::` path separator. If no such variant is found (or if the error is not a field/method expression), we give the existing suggestion that suggests adding `::TupleVariant(/* fields */)` after the enum.

<details> <summary> Enum diagnostic example </summary>

```rust
enum Foo {
    A(u32),
    B,
    C { x: u32 },
}

fn main() {
    let _ = Foo.A(42); // changed
    let _ = Foo.B;     // changed
    let _ = Foo.D(42); // no change
    let _ = Foo.D;     // no change
    let _ = Foo(42);   // no change
}
```

```diff
 error[E0423]: expected value, found enum `Foo`
  --> diag8.rs:8:13
   |
 8 |     let _ = Foo.A(42); // changed
   |             ^^^
   |
 note: the enum is defined here
  --> diag8.rs:1:1
   |
 1 | / enum Foo {
 2 | |     A(u32),
 3 | |     B,
 4 | |     C { x: u32 },
 5 | | }
   | |_^
-help: you might have meant to use the following enum variant
-  |
-8 |     let _ = Foo::B.A(42); // changed
-  |             ~~~~~~
-help: alternatively, the following enum variant is available
+help: use the path separator to refer to a variant
   |
-8 |     let _ = (Foo::A(/* fields */)).A(42); // changed
-  |             ~~~~~~~~~~~~~~~~~~~~~~
+8 |     let _ = Foo::A(42); // changed
+  |                ~~

 error[E0423]: expected value, found enum `Foo`
  --> diag8.rs:9:13
   |
 9 |     let _ = Foo.B;     // changed
   |             ^^^
   |
 note: the enum is defined here
  --> diag8.rs:1:1
   |
 1 | / enum Foo {
 2 | |     A(u32),
 3 | |     B,
 4 | |     C { x: u32 },
 5 | | }
   | |_^
-help: you might have meant to use the following enum variant
-  |
-9 |     let _ = Foo::B.B;     // changed
-  |             ~~~~~~
-help: alternatively, the following enum variant is available
+help: use the path separator to refer to a variant
   |
-9 |     let _ = (Foo::A(/* fields */)).B;     // changed
-  |             ~~~~~~~~~~~~~~~~~~~~~~
+9 |     let _ = Foo::B;     // changed
+  |                ~~

 error[E0423]: expected value, found enum `Foo`
   --> diag8.rs:10:13
    |
 10 |     let _ = Foo.D(42); // no change
    |             ^^^
    |
 note: the enum is defined here
   --> diag8.rs:1:1
    |
 1  | / enum Foo {
 2  | |     A(u32),
 3  | |     B,
 4  | |     C { x: u32 },
 5  | | }
    | |_^
 help: you might have meant to use the following enum variant
    |
 10 |     let _ = Foo::B.D(42); // no change
    |             ~~~~~~
 help: alternatively, the following enum variant is available
    |
 10 |     let _ = (Foo::A(/* fields */)).D(42); // no change
    |             ~~~~~~~~~~~~~~~~~~~~~~

 error[E0423]: expected value, found enum `Foo`
   --> diag8.rs:11:13
    |
 11 |     let _ = Foo.D;     // no change
    |             ^^^
    |
 note: the enum is defined here
   --> diag8.rs:1:1
    |
 1  | / enum Foo {
 2  | |     A(u32),
 3  | |     B,
 4  | |     C { x: u32 },
 5  | | }
    | |_^
 help: you might have meant to use the following enum variant
    |
 11 |     let _ = Foo::B.D;     // no change
    |             ~~~~~~
 help: alternatively, the following enum variant is available
    |
 11 |     let _ = (Foo::A(/* fields */)).D;     // no change
    |             ~~~~~~~~~~~~~~~~~~~~~~

 error[E0423]: expected function, tuple struct or tuple variant, found enum `Foo`
   --> diag8.rs:12:13
    |
 12 |     let _ = Foo(42);   // no change
    |             ^^^ help: try to construct one of the enum's variants: `Foo::A`
    |
    = help: you might have meant to construct the enum's non-tuple variant
 note: the enum is defined here
   --> diag8.rs:1:1
    |
 1  | / enum Foo {
 2  | |     A(u32),
 3  | |     B,
 4  | |     C { x: u32 },
 5  | | }
    | |_^

 error: aborting due to 5 previous errors
```

</details>

[^1]: or if it's a field expression and a tuple variant, that they meant to refer the variant constructor.
@jieyouxu
Copy link
Member

I think this failed in rollup: https://github.com/rust-lang/rust/actions/runs/13396817895/job/37417817710?pr=137236

 ---- [ui] tests/ui/resolve/enum-expected-value-suggest-variants.rs stdout ----
[...]
  diff of stderr:
  
  105	   | |_^
  106	help: you might have meant to use one of the following enum variants
  107	   |
  -	LL -     let _: Bar = Bar.Bad(0);
  -	LL +     let _: Bar = Bar::E.Bad(0);

@bors r-

@bors bors added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. labels Feb 18, 2025
@jieyouxu jieyouxu closed this Feb 18, 2025
@jieyouxu jieyouxu reopened this Feb 18, 2025
@rust-log-analyzer

This comment has been minimized.

When `Foo.field` or `Foo.method()` exprs are encountered, suggest `Foo::field` or `Foo::method()` when Foo is a type alias, not just
a struct, trait, or module.

Also rename test for this suggestion from issue-22692.rs to something more meaningful.
Suggest replacing `.` with `::` when encountering "expected value, found enum":
- in a method-call expression and the method has the same name as a tuple variant
- in a field-access expression and the field has the same name as a unit or tuple variant
…cro giving a type,

make it also work when the rhs is a type alias, not just a struct.
@zachs18 zachs18 force-pushed the dot_notation_more_defkinds_3 branch from 4b94807 to 2c37250 Compare February 18, 2025 19:31
@zachs18
Copy link
Contributor Author

zachs18 commented Feb 18, 2025

Probably from #136958 . Latest push should fix it.

@zachs18

This comment was marked as resolved.

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Feb 18, 2025
@davidtwco
Copy link
Member

@bors r+

@bors
Copy link
Contributor

bors commented Feb 19, 2025

📌 Commit 2c37250 has been approved by davidtwco

It is now in the queue for this repository.

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Feb 19, 2025
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this pull request Feb 19, 2025
…_3, r=davidtwco

Suggest replacing `.` with `::` in more error diagnostics.

First commit makes the existing "help: use the path separator to refer to an item" also work when the base is a type alias, not just a trait/module/struct.

The existing unconditional `DefKind::Mod | DefKind::Trait` match arm is changed to a conditional `DefKind::Mod | DefKind::Trait | DefKind::TyAlias` arm that only matches if the `path_sep` suggestion-adding closure succeeds, so as not to stop the later `DefKind::TyAlias`-specific suggestions if the path-sep suggestion does not apply. This shouldn't change behavior for `Mod` or `Trait` (due to the default arm's `return false` etc).

This commit also updates `tests/ui/resolve/issue-22692.rs` to reflect this, and also renames it to something more meaningful.

This commit also makes the `bad_struct_syntax_suggestion` closure take `err` as a parameter instead of capturing it, since otherwise caused borrowing errors due to the change to using `path_sep` in a pattern guard.

<details> <summary> Type alias diagnostic example </summary>

```rust
type S = String;

fn main() {
    let _ = S.new;
}
```

```diff
 error[E0423]: expected value, found type alias `S`
  --> diag7.rs:4:13
   |
 4 |     let _ = S.new;
   |             ^
   |
-  = note: can't use a type alias as a constructor
+  help: use the path separator to refer to an item
+  |
+4 |     let _ = S::new;
+  |              ~~
```

</details>

Second commit adds some cases for `enum`s, where if there is a field/method expression where the field/method has the name of a unit/tuple variant, we assume the user intended to create that variant[^1] and suggest replacing the `.` from the field/method suggestion with a `::` path separator. If no such variant is found (or if the error is not a field/method expression), we give the existing suggestion that suggests adding `::TupleVariant(/* fields */)` after the enum.

<details> <summary> Enum diagnostic example </summary>

```rust
enum Foo {
    A(u32),
    B,
    C { x: u32 },
}

fn main() {
    let _ = Foo.A(42); // changed
    let _ = Foo.B;     // changed
    let _ = Foo.D(42); // no change
    let _ = Foo.D;     // no change
    let _ = Foo(42);   // no change
}
```

```diff
 error[E0423]: expected value, found enum `Foo`
  --> diag8.rs:8:13
   |
 8 |     let _ = Foo.A(42); // changed
   |             ^^^
   |
 note: the enum is defined here
  --> diag8.rs:1:1
   |
 1 | / enum Foo {
 2 | |     A(u32),
 3 | |     B,
 4 | |     C { x: u32 },
 5 | | }
   | |_^
-help: you might have meant to use the following enum variant
-  |
-8 |     let _ = Foo::B.A(42); // changed
-  |             ~~~~~~
-help: alternatively, the following enum variant is available
+help: use the path separator to refer to a variant
   |
-8 |     let _ = (Foo::A(/* fields */)).A(42); // changed
-  |             ~~~~~~~~~~~~~~~~~~~~~~
+8 |     let _ = Foo::A(42); // changed
+  |                ~~

 error[E0423]: expected value, found enum `Foo`
  --> diag8.rs:9:13
   |
 9 |     let _ = Foo.B;     // changed
   |             ^^^
   |
 note: the enum is defined here
  --> diag8.rs:1:1
   |
 1 | / enum Foo {
 2 | |     A(u32),
 3 | |     B,
 4 | |     C { x: u32 },
 5 | | }
   | |_^
-help: you might have meant to use the following enum variant
-  |
-9 |     let _ = Foo::B.B;     // changed
-  |             ~~~~~~
-help: alternatively, the following enum variant is available
+help: use the path separator to refer to a variant
   |
-9 |     let _ = (Foo::A(/* fields */)).B;     // changed
-  |             ~~~~~~~~~~~~~~~~~~~~~~
+9 |     let _ = Foo::B;     // changed
+  |                ~~

 error[E0423]: expected value, found enum `Foo`
   --> diag8.rs:10:13
    |
 10 |     let _ = Foo.D(42); // no change
    |             ^^^
    |
 note: the enum is defined here
   --> diag8.rs:1:1
    |
 1  | / enum Foo {
 2  | |     A(u32),
 3  | |     B,
 4  | |     C { x: u32 },
 5  | | }
    | |_^
 help: you might have meant to use the following enum variant
    |
 10 |     let _ = Foo::B.D(42); // no change
    |             ~~~~~~
 help: alternatively, the following enum variant is available
    |
 10 |     let _ = (Foo::A(/* fields */)).D(42); // no change
    |             ~~~~~~~~~~~~~~~~~~~~~~

 error[E0423]: expected value, found enum `Foo`
   --> diag8.rs:11:13
    |
 11 |     let _ = Foo.D;     // no change
    |             ^^^
    |
 note: the enum is defined here
   --> diag8.rs:1:1
    |
 1  | / enum Foo {
 2  | |     A(u32),
 3  | |     B,
 4  | |     C { x: u32 },
 5  | | }
    | |_^
 help: you might have meant to use the following enum variant
    |
 11 |     let _ = Foo::B.D;     // no change
    |             ~~~~~~
 help: alternatively, the following enum variant is available
    |
 11 |     let _ = (Foo::A(/* fields */)).D;     // no change
    |             ~~~~~~~~~~~~~~~~~~~~~~

 error[E0423]: expected function, tuple struct or tuple variant, found enum `Foo`
   --> diag8.rs:12:13
    |
 12 |     let _ = Foo(42);   // no change
    |             ^^^ help: try to construct one of the enum's variants: `Foo::A`
    |
    = help: you might have meant to construct the enum's non-tuple variant
 note: the enum is defined here
   --> diag8.rs:1:1
    |
 1  | / enum Foo {
 2  | |     A(u32),
 3  | |     B,
 4  | |     C { x: u32 },
 5  | | }
    | |_^

 error: aborting due to 5 previous errors
```

</details>

[^1]: or if it's a field expression and a tuple variant, that they meant to refer the variant constructor.
bors added a commit to rust-lang-ci/rust that referenced this pull request Feb 19, 2025
…iaskrgr

Rollup of 9 pull requests

Successful merges:

 - rust-lang#120580 (Add `MAX_LEN_UTF8` and `MAX_LEN_UTF16` Constants)
 - rust-lang#132268 (Impl TryFrom<Vec<u8>> for String)
 - rust-lang#136093 (Match Ergonomics 2024: update old-edition behavior of feature gates)
 - rust-lang#136344 (Suggest replacing `.` with `::` in more error diagnostics.)
 - rust-lang#136690 (Use more explicit and reliable ptr select in sort impls)
 - rust-lang#136815 (CI: Stop /msys64/bin from being prepended to PATH in msys2 shell)
 - rust-lang#136923 (Lint `#[must_use]` attributes applied to methods in trait impls)
 - rust-lang#137155 (Organize `OsString`/`OsStr` shims)
 - rust-lang#137225 (vectorcall ABI: require SSE2)

Failed merges:

 - rust-lang#136780 (std: move stdio to `sys`)

r? `@ghost`
`@rustbot` modify labels: rollup
bors added a commit to rust-lang-ci/rust that referenced this pull request Feb 19, 2025
…iaskrgr

Rollup of 8 pull requests

Successful merges:

 - rust-lang#120580 (Add `MAX_LEN_UTF8` and `MAX_LEN_UTF16` Constants)
 - rust-lang#132268 (Impl TryFrom<Vec<u8>> for String)
 - rust-lang#136093 (Match Ergonomics 2024: update old-edition behavior of feature gates)
 - rust-lang#136344 (Suggest replacing `.` with `::` in more error diagnostics.)
 - rust-lang#136690 (Use more explicit and reliable ptr select in sort impls)
 - rust-lang#136815 (CI: Stop /msys64/bin from being prepended to PATH in msys2 shell)
 - rust-lang#136923 (Lint `#[must_use]` attributes applied to methods in trait impls)
 - rust-lang#137155 (Organize `OsString`/`OsStr` shims)

r? `@ghost`
`@rustbot` modify labels: rollup
@bors bors merged commit 8227910 into rust-lang:master Feb 20, 2025
6 checks passed
@rustbot rustbot added this to the 1.87.0 milestone Feb 20, 2025
rust-timer added a commit to rust-lang-ci/rust that referenced this pull request Feb 20, 2025
Rollup merge of rust-lang#136344 - zachs18:dot_notation_more_defkinds_3, r=davidtwco

Suggest replacing `.` with `::` in more error diagnostics.

First commit makes the existing "help: use the path separator to refer to an item" also work when the base is a type alias, not just a trait/module/struct.

The existing unconditional `DefKind::Mod | DefKind::Trait` match arm is changed to a conditional `DefKind::Mod | DefKind::Trait | DefKind::TyAlias` arm that only matches if the `path_sep` suggestion-adding closure succeeds, so as not to stop the later `DefKind::TyAlias`-specific suggestions if the path-sep suggestion does not apply. This shouldn't change behavior for `Mod` or `Trait` (due to the default arm's `return false` etc).

This commit also updates `tests/ui/resolve/issue-22692.rs` to reflect this, and also renames it to something more meaningful.

This commit also makes the `bad_struct_syntax_suggestion` closure take `err` as a parameter instead of capturing it, since otherwise caused borrowing errors due to the change to using `path_sep` in a pattern guard.

<details> <summary> Type alias diagnostic example </summary>

```rust
type S = String;

fn main() {
    let _ = S.new;
}
```

```diff
 error[E0423]: expected value, found type alias `S`
  --> diag7.rs:4:13
   |
 4 |     let _ = S.new;
   |             ^
   |
-  = note: can't use a type alias as a constructor
+  help: use the path separator to refer to an item
+  |
+4 |     let _ = S::new;
+  |              ~~
```

</details>

Second commit adds some cases for `enum`s, where if there is a field/method expression where the field/method has the name of a unit/tuple variant, we assume the user intended to create that variant[^1] and suggest replacing the `.` from the field/method suggestion with a `::` path separator. If no such variant is found (or if the error is not a field/method expression), we give the existing suggestion that suggests adding `::TupleVariant(/* fields */)` after the enum.

<details> <summary> Enum diagnostic example </summary>

```rust
enum Foo {
    A(u32),
    B,
    C { x: u32 },
}

fn main() {
    let _ = Foo.A(42); // changed
    let _ = Foo.B;     // changed
    let _ = Foo.D(42); // no change
    let _ = Foo.D;     // no change
    let _ = Foo(42);   // no change
}
```

```diff
 error[E0423]: expected value, found enum `Foo`
  --> diag8.rs:8:13
   |
 8 |     let _ = Foo.A(42); // changed
   |             ^^^
   |
 note: the enum is defined here
  --> diag8.rs:1:1
   |
 1 | / enum Foo {
 2 | |     A(u32),
 3 | |     B,
 4 | |     C { x: u32 },
 5 | | }
   | |_^
-help: you might have meant to use the following enum variant
-  |
-8 |     let _ = Foo::B.A(42); // changed
-  |             ~~~~~~
-help: alternatively, the following enum variant is available
+help: use the path separator to refer to a variant
   |
-8 |     let _ = (Foo::A(/* fields */)).A(42); // changed
-  |             ~~~~~~~~~~~~~~~~~~~~~~
+8 |     let _ = Foo::A(42); // changed
+  |                ~~

 error[E0423]: expected value, found enum `Foo`
  --> diag8.rs:9:13
   |
 9 |     let _ = Foo.B;     // changed
   |             ^^^
   |
 note: the enum is defined here
  --> diag8.rs:1:1
   |
 1 | / enum Foo {
 2 | |     A(u32),
 3 | |     B,
 4 | |     C { x: u32 },
 5 | | }
   | |_^
-help: you might have meant to use the following enum variant
-  |
-9 |     let _ = Foo::B.B;     // changed
-  |             ~~~~~~
-help: alternatively, the following enum variant is available
+help: use the path separator to refer to a variant
   |
-9 |     let _ = (Foo::A(/* fields */)).B;     // changed
-  |             ~~~~~~~~~~~~~~~~~~~~~~
+9 |     let _ = Foo::B;     // changed
+  |                ~~

 error[E0423]: expected value, found enum `Foo`
   --> diag8.rs:10:13
    |
 10 |     let _ = Foo.D(42); // no change
    |             ^^^
    |
 note: the enum is defined here
   --> diag8.rs:1:1
    |
 1  | / enum Foo {
 2  | |     A(u32),
 3  | |     B,
 4  | |     C { x: u32 },
 5  | | }
    | |_^
 help: you might have meant to use the following enum variant
    |
 10 |     let _ = Foo::B.D(42); // no change
    |             ~~~~~~
 help: alternatively, the following enum variant is available
    |
 10 |     let _ = (Foo::A(/* fields */)).D(42); // no change
    |             ~~~~~~~~~~~~~~~~~~~~~~

 error[E0423]: expected value, found enum `Foo`
   --> diag8.rs:11:13
    |
 11 |     let _ = Foo.D;     // no change
    |             ^^^
    |
 note: the enum is defined here
   --> diag8.rs:1:1
    |
 1  | / enum Foo {
 2  | |     A(u32),
 3  | |     B,
 4  | |     C { x: u32 },
 5  | | }
    | |_^
 help: you might have meant to use the following enum variant
    |
 11 |     let _ = Foo::B.D;     // no change
    |             ~~~~~~
 help: alternatively, the following enum variant is available
    |
 11 |     let _ = (Foo::A(/* fields */)).D;     // no change
    |             ~~~~~~~~~~~~~~~~~~~~~~

 error[E0423]: expected function, tuple struct or tuple variant, found enum `Foo`
   --> diag8.rs:12:13
    |
 12 |     let _ = Foo(42);   // no change
    |             ^^^ help: try to construct one of the enum's variants: `Foo::A`
    |
    = help: you might have meant to construct the enum's non-tuple variant
 note: the enum is defined here
   --> diag8.rs:1:1
    |
 1  | / enum Foo {
 2  | |     A(u32),
 3  | |     B,
 4  | |     C { x: u32 },
 5  | | }
    | |_^

 error: aborting due to 5 previous errors
```

</details>

[^1]: or if it's a field expression and a tuple variant, that they meant to refer the variant constructor.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-tidy Area: The tidy tool S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-bootstrap Relevant to the bootstrap subteam: Rust's build system (x.py and src/bootstrap) T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants