diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 71db58f2d8b..82e80ee3ad3 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -671,7 +671,7 @@ fn lookup_with_diagnostics( BuiltinLintDiagnostics::UnknownCrateTypes(span, note, sugg) => { db.span_suggestion(span, ¬e, sugg, Applicability::MaybeIncorrect); } - BuiltinLintDiagnostics::UnusedImports(message, replaces) => { + BuiltinLintDiagnostics::UnusedImports(message, replaces, in_test_module) => { if !replaces.is_empty() { db.tool_only_multipart_suggestion( &message, @@ -679,6 +679,14 @@ fn lookup_with_diagnostics( Applicability::MachineApplicable, ); } + + if let Some(span) = in_test_module { + let def_span = self.sess().source_map().guess_head_span(span); + db.span_help( + span.shrink_to_lo().to(def_span), + "consider adding a `#[cfg(test)]` to the containing module", + ); + } } BuiltinLintDiagnostics::RedundantImport(spans, ident) => { for (span, is_imported) in spans { diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 3f504d75dfc..96fe91b5511 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -290,7 +290,7 @@ pub enum BuiltinLintDiagnostics { ProcMacroDeriveResolutionFallback(Span), MacroExpandedMacroExportsAccessedByAbsolutePaths(Span), UnknownCrateTypes(Span, String, String), - UnusedImports(String, Vec<(Span, String)>), + UnusedImports(String, Vec<(Span, String)>, Option), RedundantImport(Vec<(Span, bool)>, Ident), DeprecatedMacro(Option, Span), MissingAbi(Span, Abi), diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index d45c064d5e3..74edc3a2d5e 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -108,7 +108,7 @@ impl<'a> Resolver<'a> { /// Reachable macros with block module parents exist due to `#[macro_export] macro_rules!`, /// but they cannot use def-site hygiene, so the assumption holds /// (). - fn get_nearest_non_block_module(&mut self, mut def_id: DefId) -> Module<'a> { + crate fn get_nearest_non_block_module(&mut self, mut def_id: DefId) -> Module<'a> { loop { match self.get_module(def_id) { Some(module) => return module, diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs index 63699128e9e..601f2d96ff5 100644 --- a/compiler/rustc_resolve/src/check_unused.rs +++ b/compiler/rustc_resolve/src/check_unused.rs @@ -24,6 +24,7 @@ // in the last step use crate::imports::ImportKind; +use crate::module_to_string; use crate::Resolver; use rustc_ast as ast; @@ -314,12 +315,29 @@ impl Resolver<'_> { "remove the unused import" }; + let parent_module = visitor.r.get_nearest_non_block_module( + visitor.r.local_def_id(unused.use_tree_id).to_def_id(), + ); + let test_module_span = match module_to_string(parent_module) { + Some(module) + if module == "test" + || module == "tests" + || module.starts_with("test_") + || module.starts_with("tests_") + || module.ends_with("_test") + || module.ends_with("_tests") => + { + Some(parent_module.span) + } + _ => None, + }; + visitor.r.lint_buffer.buffer_lint_with_diagnostic( UNUSED_IMPORTS, unused.use_tree_id, ms, &msg, - BuiltinLintDiagnostics::UnusedImports(fix_msg.into(), fixes), + BuiltinLintDiagnostics::UnusedImports(fix_msg.into(), fixes, test_module_span), ); } } diff --git a/src/test/ui/imports/unused-imports-in-test-module.rs b/src/test/ui/imports/unused-imports-in-test-module.rs new file mode 100644 index 00000000000..b003b99b6cf --- /dev/null +++ b/src/test/ui/imports/unused-imports-in-test-module.rs @@ -0,0 +1,64 @@ +#![deny(unused_imports)] + +use std::io::BufRead; //~ ERROR unused import: `std::io::BufRead` + +fn a() {} +fn b() {} + +mod test { + use super::a; //~ ERROR unused import: `super::a` + + fn foo() { + use crate::b; //~ ERROR unused import: `crate::b` + } +} + +mod tests { + use super::a; //~ ERROR unused import: `super::a` + + fn foo() { + use crate::b; //~ ERROR unused import: `crate::b` + } +} + +mod test_a { + use super::a; //~ ERROR unused import: `super::a` + + fn foo() { + use crate::b; //~ ERROR unused import: `crate::b` + } +} + +mod a_test { + use super::a; //~ ERROR unused import: `super::a` + + fn foo() { + use crate::b; //~ ERROR unused import: `crate::b` + } +} + +mod tests_a { + use super::a; //~ ERROR unused import: `super::a` + + fn foo() { + use crate::b; //~ ERROR unused import: `crate::b` + } +} + +mod a_tests { + use super::a; //~ ERROR unused import: `super::a` + + fn foo() { + use crate::b; //~ ERROR unused import: `crate::b` + } +} + +mod fastest_search { + use super::a; //~ ERROR unused import: `super::a` + + fn foo() { + use crate::b; //~ ERROR unused import: `crate::b` + } +} + +fn main() {} diff --git a/src/test/ui/imports/unused-imports-in-test-module.stderr b/src/test/ui/imports/unused-imports-in-test-module.stderr new file mode 100644 index 00000000000..2efea5b3609 --- /dev/null +++ b/src/test/ui/imports/unused-imports-in-test-module.stderr @@ -0,0 +1,170 @@ +error: unused import: `std::io::BufRead` + --> $DIR/unused-imports-in-test-module.rs:3:5 + | +LL | use std::io::BufRead; + | ^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/unused-imports-in-test-module.rs:1:9 + | +LL | #![deny(unused_imports)] + | ^^^^^^^^^^^^^^ + +error: unused import: `super::a` + --> $DIR/unused-imports-in-test-module.rs:9:9 + | +LL | use super::a; + | ^^^^^^^^ + | +help: consider adding a `#[cfg(test)]` to the containing module + --> $DIR/unused-imports-in-test-module.rs:8:1 + | +LL | mod test { + | ^^^^^^^^ + +error: unused import: `crate::b` + --> $DIR/unused-imports-in-test-module.rs:12:13 + | +LL | use crate::b; + | ^^^^^^^^ + | +help: consider adding a `#[cfg(test)]` to the containing module + --> $DIR/unused-imports-in-test-module.rs:8:1 + | +LL | mod test { + | ^^^^^^^^ + +error: unused import: `super::a` + --> $DIR/unused-imports-in-test-module.rs:17:9 + | +LL | use super::a; + | ^^^^^^^^ + | +help: consider adding a `#[cfg(test)]` to the containing module + --> $DIR/unused-imports-in-test-module.rs:16:1 + | +LL | mod tests { + | ^^^^^^^^^ + +error: unused import: `crate::b` + --> $DIR/unused-imports-in-test-module.rs:20:13 + | +LL | use crate::b; + | ^^^^^^^^ + | +help: consider adding a `#[cfg(test)]` to the containing module + --> $DIR/unused-imports-in-test-module.rs:16:1 + | +LL | mod tests { + | ^^^^^^^^^ + +error: unused import: `super::a` + --> $DIR/unused-imports-in-test-module.rs:25:9 + | +LL | use super::a; + | ^^^^^^^^ + | +help: consider adding a `#[cfg(test)]` to the containing module + --> $DIR/unused-imports-in-test-module.rs:24:1 + | +LL | mod test_a { + | ^^^^^^^^^^ + +error: unused import: `crate::b` + --> $DIR/unused-imports-in-test-module.rs:28:13 + | +LL | use crate::b; + | ^^^^^^^^ + | +help: consider adding a `#[cfg(test)]` to the containing module + --> $DIR/unused-imports-in-test-module.rs:24:1 + | +LL | mod test_a { + | ^^^^^^^^^^ + +error: unused import: `super::a` + --> $DIR/unused-imports-in-test-module.rs:33:9 + | +LL | use super::a; + | ^^^^^^^^ + | +help: consider adding a `#[cfg(test)]` to the containing module + --> $DIR/unused-imports-in-test-module.rs:32:1 + | +LL | mod a_test { + | ^^^^^^^^^^ + +error: unused import: `crate::b` + --> $DIR/unused-imports-in-test-module.rs:36:13 + | +LL | use crate::b; + | ^^^^^^^^ + | +help: consider adding a `#[cfg(test)]` to the containing module + --> $DIR/unused-imports-in-test-module.rs:32:1 + | +LL | mod a_test { + | ^^^^^^^^^^ + +error: unused import: `super::a` + --> $DIR/unused-imports-in-test-module.rs:41:9 + | +LL | use super::a; + | ^^^^^^^^ + | +help: consider adding a `#[cfg(test)]` to the containing module + --> $DIR/unused-imports-in-test-module.rs:40:1 + | +LL | mod tests_a { + | ^^^^^^^^^^^ + +error: unused import: `crate::b` + --> $DIR/unused-imports-in-test-module.rs:44:13 + | +LL | use crate::b; + | ^^^^^^^^ + | +help: consider adding a `#[cfg(test)]` to the containing module + --> $DIR/unused-imports-in-test-module.rs:40:1 + | +LL | mod tests_a { + | ^^^^^^^^^^^ + +error: unused import: `super::a` + --> $DIR/unused-imports-in-test-module.rs:49:9 + | +LL | use super::a; + | ^^^^^^^^ + | +help: consider adding a `#[cfg(test)]` to the containing module + --> $DIR/unused-imports-in-test-module.rs:48:1 + | +LL | mod a_tests { + | ^^^^^^^^^^^ + +error: unused import: `crate::b` + --> $DIR/unused-imports-in-test-module.rs:52:13 + | +LL | use crate::b; + | ^^^^^^^^ + | +help: consider adding a `#[cfg(test)]` to the containing module + --> $DIR/unused-imports-in-test-module.rs:48:1 + | +LL | mod a_tests { + | ^^^^^^^^^^^ + +error: unused import: `super::a` + --> $DIR/unused-imports-in-test-module.rs:57:9 + | +LL | use super::a; + | ^^^^^^^^ + +error: unused import: `crate::b` + --> $DIR/unused-imports-in-test-module.rs:60:13 + | +LL | use crate::b; + | ^^^^^^^^ + +error: aborting due to 15 previous errors +