Skip to content

Commit 737b8be

Browse files
authored
Fix std_instead_of_core FP when part of the use cannot be replaced (#15016)
Closes rust-lang/rust-clippy#14982 ---- changelog: [`std_instead_of_core`] fix FP when part of the `use` cannot be replaced
2 parents a1a1393 + d98070d commit 737b8be

File tree

3 files changed

+63
-28
lines changed

3 files changed

+63
-28
lines changed

clippy_lints/src/std_instead_of_core.rs

Lines changed: 51 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
use clippy_config::Conf;
2-
use clippy_utils::diagnostics::span_lint_and_then;
2+
use clippy_utils::diagnostics::span_lint_and_sugg;
33
use clippy_utils::is_from_proc_macro;
44
use clippy_utils::msrvs::Msrv;
55
use rustc_attr_data_structures::{StabilityLevel, StableSince};
66
use rustc_errors::Applicability;
77
use rustc_hir::def::Res;
88
use rustc_hir::def_id::DefId;
9-
use rustc_hir::{HirId, Path, PathSegment};
10-
use rustc_lint::{LateContext, LateLintPass, LintContext};
9+
use rustc_hir::{Block, Body, HirId, Path, PathSegment};
10+
use rustc_lint::{LateContext, LateLintPass, Lint, LintContext};
1111
use rustc_session::impl_lint_pass;
1212
use rustc_span::symbol::kw;
1313
use rustc_span::{Span, sym};
@@ -88,24 +88,35 @@ declare_clippy_lint! {
8888
}
8989

9090
pub struct StdReexports {
91-
// Paths which can be either a module or a macro (e.g. `std::env`) will cause this check to happen
92-
// twice. First for the mod, second for the macro. This is used to avoid the lint reporting for the macro
93-
// when the path could be also be used to access the module.
94-
prev_span: Span,
91+
lint_point: (Span, Option<LintPoint>),
9592
msrv: Msrv,
9693
}
9794

9895
impl StdReexports {
9996
pub fn new(conf: &'static Conf) -> Self {
10097
Self {
101-
prev_span: Span::default(),
98+
lint_point: Default::default(),
10299
msrv: conf.msrv,
103100
}
104101
}
102+
103+
fn lint_if_finish(&mut self, cx: &LateContext<'_>, (span, item): (Span, Option<LintPoint>)) {
104+
if span.source_equal(self.lint_point.0) {
105+
return;
106+
}
107+
108+
if !self.lint_point.0.is_dummy() {
109+
emit_lints(cx, &self.lint_point);
110+
}
111+
112+
self.lint_point = (span, item);
113+
}
105114
}
106115

107116
impl_lint_pass!(StdReexports => [STD_INSTEAD_OF_CORE, STD_INSTEAD_OF_ALLOC, ALLOC_INSTEAD_OF_CORE]);
108117

118+
type LintPoint = (&'static Lint, &'static str, &'static str);
119+
109120
impl<'tcx> LateLintPass<'tcx> for StdReexports {
110121
fn check_path(&mut self, cx: &LateContext<'tcx>, path: &Path<'tcx>, _: HirId) {
111122
if let Res::Def(_, def_id) = path.res
@@ -119,40 +130,52 @@ impl<'tcx> LateLintPass<'tcx> for StdReexports {
119130
sym::core => (STD_INSTEAD_OF_CORE, "std", "core"),
120131
sym::alloc => (STD_INSTEAD_OF_ALLOC, "std", "alloc"),
121132
_ => {
122-
self.prev_span = first_segment.ident.span;
133+
self.lint_if_finish(cx, (first_segment.ident.span, None));
123134
return;
124135
},
125136
},
126137
sym::alloc => {
127138
if cx.tcx.crate_name(def_id.krate) == sym::core {
128139
(ALLOC_INSTEAD_OF_CORE, "alloc", "core")
129140
} else {
130-
self.prev_span = first_segment.ident.span;
141+
self.lint_if_finish(cx, (first_segment.ident.span, None));
131142
return;
132143
}
133144
},
134145
_ => return,
135146
};
136-
if first_segment.ident.span != self.prev_span {
137-
#[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
138-
span_lint_and_then(
139-
cx,
140-
lint,
141-
first_segment.ident.span,
142-
format!("used import from `{used_mod}` instead of `{replace_with}`"),
143-
|diag| {
144-
diag.span_suggestion(
145-
first_segment.ident.span,
146-
format!("consider importing the item from `{replace_with}`"),
147-
replace_with.to_string(),
148-
Applicability::MachineApplicable,
149-
);
150-
},
151-
);
152-
self.prev_span = first_segment.ident.span;
153-
}
147+
148+
self.lint_if_finish(cx, (first_segment.ident.span, Some((lint, used_mod, replace_with))));
154149
}
155150
}
151+
152+
fn check_block_post(&mut self, cx: &LateContext<'tcx>, _: &Block<'tcx>) {
153+
self.lint_if_finish(cx, Default::default());
154+
}
155+
156+
fn check_body_post(&mut self, cx: &LateContext<'tcx>, _: &Body<'tcx>) {
157+
self.lint_if_finish(cx, Default::default());
158+
}
159+
160+
fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
161+
self.lint_if_finish(cx, Default::default());
162+
}
163+
}
164+
165+
fn emit_lints(cx: &LateContext<'_>, (span, item): &(Span, Option<LintPoint>)) {
166+
let Some((lint, used_mod, replace_with)) = item else {
167+
return;
168+
};
169+
170+
span_lint_and_sugg(
171+
cx,
172+
lint,
173+
*span,
174+
format!("used import from `{used_mod}` instead of `{replace_with}`"),
175+
format!("consider importing the item from `{replace_with}`"),
176+
(*replace_with).to_string(),
177+
Applicability::MachineApplicable,
178+
);
156179
}
157180

158181
/// Returns the first named segment of a [`Path`].

tests/ui/std_instead_of_core.fixed

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,3 +90,9 @@ fn msrv_1_76(_: std::net::IpAddr) {}
9090
#[clippy::msrv = "1.77"]
9191
fn msrv_1_77(_: core::net::IpAddr) {}
9292
//~^ std_instead_of_core
93+
94+
#[warn(clippy::std_instead_of_core)]
95+
#[rustfmt::skip]
96+
fn issue14982() {
97+
use std::{collections::HashMap, hash::Hash};
98+
}

tests/ui/std_instead_of_core.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,3 +90,9 @@ fn msrv_1_76(_: std::net::IpAddr) {}
9090
#[clippy::msrv = "1.77"]
9191
fn msrv_1_77(_: std::net::IpAddr) {}
9292
//~^ std_instead_of_core
93+
94+
#[warn(clippy::std_instead_of_core)]
95+
#[rustfmt::skip]
96+
fn issue14982() {
97+
use std::{collections::HashMap, hash::Hash};
98+
}

0 commit comments

Comments
 (0)