fix incorrect suggestions in private import diagnostic rust-lang/rust#156244

Merged

23 comments and reviews loaded in 1.04s

el-ev Avatar

View all comments

Resolves #156060.

  1. In nested imports like use two::{One, ...}, the diagnostic suggested replacing the One with a multi-segment path of a different module, producing invalid code like use crate::two::{one::One, Two}. Skip it when single_nested == true.
  2. Stop unconditionally skipping the first segment of import.module_path, which can produce incorrect paths in edition 2018 and later.
  3. Mark the suggestion as "directly" instead of "through the re-export" when the import's source is the definition itself.
rustbot Avatar

r? @jdonszelmann

rustbot has assigned @jdonszelmann.
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

Why was this reviewer chosen?

The reviewer was selected based on:

  • Owners of files modified in this PR: compiler
  • compiler expanded to 73 candidates
  • Random selection from 19 candidates
mu001999 Avatar
mu001999 left a comment · edited
View on GitHub

By the way, the following will also emit an invalid suggestion (invalid after edition 2015), because we skipped the first segment:

mod one {
    pub struct One;
}

mod two {
    use crate::one::One;
}

mod test {
    use crate::two::One;
}

View changes since this review

compiler/rustc_resolve/src/diagnostics.rs · outdated

Could you merge the check of single_nested into here? Or after this but before if sugg.len() <= 1? Because it could break early

Moved. Also fixed the other invalid suggestion by dropping .skip(1).

Found another diagnostic issue:

help: import `One` through the re-export
   |
LL -     use crate::two::One;
LL +     use crate::one::One;
   |

Actually, crate::one::One is not a re-export; it is the definition.

Moved. Also fixed the other invalid suggestion by dropping .skip(1).

Found another diagnostic issue:

help: import ``One`` through the re-export
   |
LL -     use crate::two::One;
LL +     use crate::one::One;
   |

Actually, crate::one::One is not a re-export; it is the definition.

Fixed that.

rust-log-analyzer Avatar
rust-log-analyzer on 2026-05-07 16:27:41 UTC · hidden as outdated
View on GitHub

The job aarch64-gnu-llvm-21-1 failed! Check out the build log: (web) (plain enhanced) (plain)

Click to see the possible cause of the failure (guessed by this bot)
##[endgroup]
Executing "/scripts/stage_2_test_set1.sh"
+ /scripts/stage_2_test_set1.sh
+ '[' 1 == 1 ']'
+ echo 'PR_CI_JOB set; skipping tidy'
+ SKIP_TIDY='--skip tidy'
+ ../x.py --stage 2 test --skip tidy --skip compiler --skip src
PR_CI_JOB set; skipping tidy
##[group]Building bootstrap
    Finished `dev` profile [unoptimized] target(s) in 0.04s
##[endgroup]
---
---- [ui] tests/ui/shadowed/shadowed-use-visibility.rs stdout ----
Saved the actual stderr to `/checkout/obj/build/aarch64-unknown-linux-gnu/test/ui/shadowed/shadowed-use-visibility/shadowed-use-visibility.stderr`
diff of stderr:

19 LL -     use crate::foo::bar::f as g;
20 LL +     use foo::f as g;
21    |
+ help: import `bar` directly
+    |
+ LL -     use crate::foo::bar::f as g;
+ LL +     use crate::bar;
+    |
22 
23 error[E0603]: module import `f` is private
24   --> $DIR/shadowed-use-visibility.rs:15:10

40    |
41 LL - use bar::f::f;
42 LL + use bar::f;
+    |
+ help: import `f` directly
+    |
+ LL - use bar::f::f;
+ LL + use crate::f;
43    |
44 
45 error: aborting due to 2 previous errors

Note: some mismatched output was normalized before being compared
- LL -     use crate::foo::bar::f as g; //~ ERROR module import `bar` is private
- LL +     use crate::bar; //~ ERROR module import `bar` is private
- LL - use bar::f::f; //~ ERROR module import `f` is private
- LL + use crate::f; //~ ERROR module import `f` is private
+ help: import `bar` directly
+    |
+ LL -     use crate::foo::bar::f as g;
+ LL +     use crate::bar;
+    |
+    |
+ help: import `f` directly
+    |
+ LL - use bar::f::f;
+ LL + use crate::f;


The actual stderr differed from the expected stderr
To update references, rerun the tests and pass the `--bless` flag
To only update this specific test, also pass `--test-args shadowed/shadowed-use-visibility.rs`

error: 1 errors occurred comparing output.
status: exit status: 1
command: env -u RUSTC_LOG_COLOR RUSTC_ICE="0" RUST_BACKTRACE="short" "/checkout/obj/build/aarch64-unknown-linux-gnu/stage2/bin/rustc" "/checkout/tests/ui/shadowed/shadowed-use-visibility.rs" "-Zthreads=1" "-Zsimulate-remapped-rust-src-base=/rustc/FAKE_PREFIX" "-Ztranslate-remapped-path-to-local-path=no" "-Z" "ignore-directory-in-diagnostics-source-blocks=/cargo" "-Z" "ignore-directory-in-diagnostics-source-blocks=/checkout/vendor" "--sysroot" "/checkout/obj/build/aarch64-unknown-linux-gnu/stage2" "--target=aarch64-unknown-linux-gnu" "--check-cfg" "cfg(test,FALSE)" "--error-format" "json" "--json" "future-incompat" "-Ccodegen-units=1" "-Zui-testing" "-Zdeduplicate-diagnostics=no" "-Zwrite-long-types-to-disk=no" "-Cstrip=debuginfo" "--emit" "metadata" "-C" "prefer-dynamic" "--out-dir" "/checkout/obj/build/aarch64-unknown-linux-gnu/test/ui/shadowed/shadowed-use-visibility" "-A" "unused" "-W" "unused_attributes" "-A" "internal_features" "-A" "incomplete_features" "-A" "unused_parens" "-A" "unused_braces" "-Crpath" "-Cdebuginfo=0" "-Lnative=/checkout/obj/build/aarch64-unknown-linux-gnu/native/rust-test-helpers"
stdout: none
--- stderr -------------------------------
error[E0603]: module import `bar` is private
##[error]  --> /checkout/tests/ui/shadowed/shadowed-use-visibility.rs:9:21
   |
LL |     use crate::foo::bar::f as g; //~ ERROR module import `bar` is private
   |                     ^^^ private module import
   |
note: the module import `bar` is defined here...
  --> /checkout/tests/ui/shadowed/shadowed-use-visibility.rs:4:9
   |
---
LL | mod foo {
   | ^^^^^^^
help: consider importing this function instead
   |
LL -     use crate::foo::bar::f as g; //~ ERROR module import `bar` is private
LL +     use foo::f as g; //~ ERROR module import `bar` is private
   |
help: import `bar` directly
   |
LL -     use crate::foo::bar::f as g; //~ ERROR module import `bar` is private
LL +     use crate::bar; //~ ERROR module import `bar` is private
   |

error[E0603]: module import `f` is private
##[error]  --> /checkout/tests/ui/shadowed/shadowed-use-visibility.rs:15:10
   |
LL | use bar::f::f; //~ ERROR module import `f` is private
   |          ^ private module import
   |
note: the module import `f` is defined here...
  --> /checkout/tests/ui/shadowed/shadowed-use-visibility.rs:11:9
   |
LL |     use crate::foo as f;
---
LL | mod foo {
   | ^^^^^^^
help: consider importing this function through its public re-export instead
   |
LL - use bar::f::f; //~ ERROR module import `f` is private
LL + use bar::f; //~ ERROR module import `f` is private
   |
help: import `f` directly
   |
LL - use bar::f::f; //~ ERROR module import `f` is private
LL + use crate::f; //~ ERROR module import `f` is private
   |

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0603`.
el-ev Avatar
mu001999 Avatar
compiler/rustc_resolve/src/diagnostics.rs · resolved
2479 //
2480 // See issue #156060.
2481 let can_replace_use =
2482 !single_nested && !outermost_res.is_some_and(|(_, outer)| outer.span != ident.span);

Why does this new condition not need to check point_to_def anymore?

View changes since the review

point_to_def is irrelevant here. We want a suggestion here as long as its valid, regardless of whether a public re-export was found.

Will this result in fewer suggestions being emitted? IIUC, this condition is more conservative than before. Before we only break when !show_candidates && ident.span != outer_ident.span. But now, we will break even if there are available candidates.

mu001999 Avatar
compiler/rustc_resolve/src/diagnostics.rs · outdated

Could you remove this? Seems this is only used at line 2434 and 2439. And the logic is weird, because seems there is no place to clear it.

View changes since the review

mu001999 Avatar
compiler/rustc_resolve/src/diagnostics.rs · outdated
2429 let mut path = Vec::new();
2430 // Don't include `{{root}}` in suggestions - it's an internal symbol
2431 // that should never be shown to users.
2432 for segment in import
2433 .module_path
2434 .iter()
2435 .skip_while(|segment| segment.ident.name == kw::PathRoot)
2436 {
2437 path.push(segment.ident);
2438 2438 }
2439 sugg_paths.push((
2440 path.iter().cloned().chain(std::iter::once(ident)).collect::<Vec<_>>(),
2441 true, // re-export
2442 ));
2439 path.push(ident);
2440 let through_reexport = !matches!(source_decl.kind, DeclKind::Def(_));
2441 sugg_paths.push((path, through_reexport));
Suggested change
let mut path = Vec::new();
// Don't include `{{root}}` in suggestions - it's an internal symbol
// that should never be shown to users.
for segment in import
.module_path
.iter()
.skip_while(|segment| segment.ident.name == kw::PathRoot)
{
path.push(segment.ident);
}
sugg_paths.push((
path.iter().cloned().chain(std::iter::once(ident)).collect::<Vec<_>>(),
true, // re-export
));
path.push(ident);
let through_reexport = !matches!(source_decl.kind, DeclKind::Def(_));
sugg_paths.push((path, through_reexport));
// Don't include `{{root}}` in suggestions - it's an internal symbol
// that should never be shown to users.
let path = import.module_path
.iter().cloned()
.filter(|seg| seg.ident.name != kw::PathRoot)
.chain(std::iter::once(ident))
.collect::<Vec<_>>();
let through_reexport = !matches!(source_decl.kind, DeclKind::Def(_));
sugg_paths.push((path, through_reexport));

View changes since the review

That doesn't compile. Rewrote as

let path = import
    .module_path
    .iter()
    .filter(|seg| seg.ident.name != kw::PathRoot)
    .map(|seg| seg.ident.clone())
    .chain(std::iter::once(ident))
    .collect::<Vec<_>>();
rust-log-analyzer Avatar
rust-log-analyzer on 2026-05-08 09:22:48 UTC · hidden as outdated
View on GitHub

The job aarch64-gnu-llvm-21-1 failed! Check out the build log: (web) (plain enhanced) (plain)

Click to see the possible cause of the failure (guessed by this bot)
##[endgroup]
Executing "/scripts/stage_2_test_set1.sh"
+ /scripts/stage_2_test_set1.sh
+ '[' 1 == 1 ']'
+ echo 'PR_CI_JOB set; skipping tidy'
+ SKIP_TIDY='--skip tidy'
+ ../x.py --stage 2 test --skip tidy --skip compiler --skip src
PR_CI_JOB set; skipping tidy
##[group]Building bootstrap
    Finished `dev` profile [unoptimized] target(s) in 0.04s
##[endgroup]
---
28 error: aborting due to 1 previous error
29 

Note: some mismatched output was normalized before being compared
- LL - pub use parser::ParseOptions; //~ ERROR struct import `ParseOptions` is private
- LL + pub use options::ParseOptions; //~ ERROR struct import `ParseOptions` is private
+ help: import `ParseOptions` directly
+    |
+ LL - pub use parser::ParseOptions;
+ LL + pub use options::ParseOptions;
+    |


The actual stderr differed from the expected stderr
To update references, rerun the tests and pass the `--bless` flag
To only update this specific test, also pass `--test-args imports/issue-55884-2.rs`

error: 1 errors occurred comparing output.
status: exit status: 1
command: env -u RUSTC_LOG_COLOR RUSTC_ICE="0" RUST_BACKTRACE="short" "/checkout/obj/build/aarch64-unknown-linux-gnu/stage2/bin/rustc" "/checkout/tests/ui/imports/issue-55884-2.rs" "-Zthreads=1" "-Zsimulate-remapped-rust-src-base=/rustc/FAKE_PREFIX" "-Ztranslate-remapped-path-to-local-path=no" "-Z" "ignore-directory-in-diagnostics-source-blocks=/cargo" "-Z" "ignore-directory-in-diagnostics-source-blocks=/checkout/vendor" "--sysroot" "/checkout/obj/build/aarch64-unknown-linux-gnu/stage2" "--target=aarch64-unknown-linux-gnu" "--check-cfg" "cfg(test,FALSE)" "--error-format" "json" "--json" "future-incompat" "-Ccodegen-units=1" "-Zui-testing" "-Zdeduplicate-diagnostics=no" "-Zwrite-long-types-to-disk=no" "-Cstrip=debuginfo" "--emit" "metadata" "-C" "prefer-dynamic" "--out-dir" "/checkout/obj/build/aarch64-unknown-linux-gnu/test/ui/imports/issue-55884-2" "-A" "unused" "-W" "unused_attributes" "-A" "internal_features" "-A" "incomplete_features" "-A" "unused_parens" "-A" "unused_braces" "-Crpath" "-Cdebuginfo=0" "-Lnative=/checkout/obj/build/aarch64-unknown-linux-gnu/native/rust-test-helpers" "--edition=2015"
stdout: none
--- stderr -------------------------------
error[E0603]: struct import `ParseOptions` is private
##[error]  --> /checkout/tests/ui/imports/issue-55884-2.rs:14:17
   |
LL | pub use parser::ParseOptions; //~ ERROR struct import `ParseOptions` is private
   |                 ^^^^^^^^^^^^ private struct import
   |
note: the struct import `ParseOptions` is defined here...
  --> /checkout/tests/ui/imports/issue-55884-2.rs:11:9
   |
LL |     use ParseOptions;
   |         ^^^^^^^^^^^^
note: ...and refers to the struct import `ParseOptions` which is defined here...
  --> /checkout/tests/ui/imports/issue-55884-2.rs:14:9
   |
LL | pub use parser::ParseOptions; //~ ERROR struct import `ParseOptions` is private
   |         ^^^^^^^^^^^^^^^^^^^^ you could import this re-export
note: ...and refers to the struct import `ParseOptions` which is defined here...
  --> /checkout/tests/ui/imports/issue-55884-2.rs:7:13
   |
LL |     pub use options::*;
   |             ^^^^^^^^^^ you could import this re-export
note: ...and refers to the struct `ParseOptions` which is defined here
  --> /checkout/tests/ui/imports/issue-55884-2.rs:3:5
   |
LL |     pub struct ParseOptions {}
   |     ^^^^^^^^^^^^^^^^^^^^^^^ you could import this directly
help: import `ParseOptions` directly
   |
LL - pub use parser::ParseOptions; //~ ERROR struct import `ParseOptions` is private
LL + pub use options::ParseOptions; //~ ERROR struct import `ParseOptions` is private
   |

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0603`.
el-ev Avatar
tests/ui/imports/private-import-no-suggestion-156244.stderr · outdated
51 help: consider importing this struct instead
52 |
53 LL - use crate::rename::inner::Item;
54 LL + use outer::actual::Item;

nothing. forgot //@ edition:2018

mu001999 Avatar
tests/ui/imports/private-import-nested-suggestion-156060.rs · resolved
1 // Regression test for #156060.

Could you combine other tests into this, because some are duplicate. You can use //@ revisions (revisions directive) for different editions, e.g., https://github.com/rust-lang/rust/blob/main/tests/ui/use/use-path-segment-kw.rs#L1-L3.

View changes since the review

mu001999 Avatar
mu001999 Avatar
mu001999 left a comment · edited
View on GitHub
rust-bors Avatar

📌 Commit 6d5fc32 has been approved by mu001999

It is now in the queue for this repository.