Skip to content

Commit 6373fd3

Browse files
committed
Merge branch 'master' into new-lint-rwlock_atomic
2 parents db3a5b9 + c48592e commit 6373fd3

34 files changed

+1580
-1125
lines changed

clippy_dev/src/new_lint.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -157,18 +157,19 @@ fn add_lint(lint: &LintData<'_>, enable_msrv: bool) -> io::Result<()> {
157157
let path = "clippy_lints/src/lib.rs";
158158
let mut lib_rs = fs::read_to_string(path).context("reading")?;
159159

160-
let comment_start = lib_rs.find("// add lints here,").expect("Couldn't find comment");
161-
let ctor_arg = if lint.pass == Pass::Late { "_" } else { "" };
162-
let lint_pass = lint.pass;
160+
let (comment, ctor_arg) = if lint.pass == Pass::Late {
161+
("// add late passes here", "_")
162+
} else {
163+
("// add early passes here", "")
164+
};
165+
let comment_start = lib_rs.find(comment).expect("Couldn't find comment");
163166
let module_name = lint.name;
164167
let camel_name = to_camel_case(lint.name);
165168

166169
let new_lint = if enable_msrv {
167-
format!(
168-
"store.register_{lint_pass}_pass(move |{ctor_arg}| Box::new({module_name}::{camel_name}::new(conf)));\n ",
169-
)
170+
format!("Box::new(move |{ctor_arg}| Box::new({module_name}::{camel_name}::new(conf))),\n ",)
170171
} else {
171-
format!("store.register_{lint_pass}_pass(|{ctor_arg}| Box::new({module_name}::{camel_name}));\n ",)
172+
format!("Box::new(|{ctor_arg}| Box::new({module_name}::{camel_name})),\n ",)
172173
};
173174

174175
lib_rs.insert_str(comment_start, &new_lint);

clippy_lints/src/incompatible_msrv.rs

Lines changed: 45 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
use clippy_config::Conf;
22
use clippy_utils::diagnostics::span_lint_and_then;
33
use clippy_utils::msrvs::Msrv;
4-
use clippy_utils::{is_in_const_context, is_in_test};
4+
use clippy_utils::{is_in_const_context, is_in_test, sym};
55
use rustc_data_structures::fx::FxHashMap;
66
use rustc_hir::{self as hir, AmbigArg, Expr, ExprKind, HirId, RustcVersion, StabilityLevel, StableSince};
77
use rustc_lint::{LateContext, LateLintPass};
88
use rustc_middle::ty::{self, TyCtxt};
99
use rustc_session::impl_lint_pass;
1010
use rustc_span::def_id::{CrateNum, DefId};
11-
use rustc_span::{ExpnKind, Span, sym};
11+
use rustc_span::{ExpnKind, Span};
1212

1313
declare_clippy_lint! {
1414
/// ### What it does
@@ -77,11 +77,36 @@ enum Availability {
7777
Since(RustcVersion),
7878
}
7979

80+
/// All known std crates containing a stability attribute.
81+
struct StdCrates([Option<CrateNum>; 6]);
82+
impl StdCrates {
83+
fn new(tcx: TyCtxt<'_>) -> Self {
84+
let mut res = Self([None; _]);
85+
for &krate in tcx.crates(()) {
86+
// FIXME(@Jarcho): We should have an internal lint to detect when this list is out of date.
87+
match tcx.crate_name(krate) {
88+
sym::alloc => res.0[0] = Some(krate),
89+
sym::core => res.0[1] = Some(krate),
90+
sym::core_arch => res.0[2] = Some(krate),
91+
sym::proc_macro => res.0[3] = Some(krate),
92+
sym::std => res.0[4] = Some(krate),
93+
sym::std_detect => res.0[5] = Some(krate),
94+
_ => {},
95+
}
96+
}
97+
res
98+
}
99+
100+
fn contains(&self, krate: CrateNum) -> bool {
101+
self.0.contains(&Some(krate))
102+
}
103+
}
104+
80105
pub struct IncompatibleMsrv {
81106
msrv: Msrv,
82107
availability_cache: FxHashMap<(DefId, bool), Availability>,
83108
check_in_tests: bool,
84-
core_crate: Option<CrateNum>,
109+
std_crates: StdCrates,
85110

86111
// The most recently called path. Used to skip checking the path after it's
87112
// been checked when visiting the call expression.
@@ -96,11 +121,7 @@ impl IncompatibleMsrv {
96121
msrv: conf.msrv,
97122
availability_cache: FxHashMap::default(),
98123
check_in_tests: conf.check_incompatible_msrv_in_tests,
99-
core_crate: tcx
100-
.crates(())
101-
.iter()
102-
.find(|krate| tcx.crate_name(**krate) == sym::core)
103-
.copied(),
124+
std_crates: StdCrates::new(tcx),
104125
called_path: None,
105126
}
106127
}
@@ -152,21 +173,24 @@ impl IncompatibleMsrv {
152173
node: HirId,
153174
span: Span,
154175
) {
155-
if def_id.is_local() {
156-
// We don't check local items since their MSRV is supposed to always be valid.
176+
if !self.std_crates.contains(def_id.krate) {
177+
// No stability attributes to lookup for these items.
157178
return;
158179
}
159-
let expn_data = span.ctxt().outer_expn_data();
160-
if let ExpnKind::AstPass(_) | ExpnKind::Desugaring(_) = expn_data.kind {
161-
// Desugared expressions get to cheat and stability is ignored.
162-
// Intentionally not using `.from_expansion()`, since we do still care about macro expansions
163-
return;
164-
}
165-
// Functions coming from `core` while expanding a macro such as `assert*!()` get to cheat too: the
166-
// macros may have existed prior to the checked MSRV, but their expansion with a recent compiler
167-
// might use recent functions or methods. Compiling with an older compiler would not use those.
168-
if Some(def_id.krate) == self.core_crate && expn_data.macro_def_id.map(|did| did.krate) == self.core_crate {
169-
return;
180+
// Use `from_expansion` to fast-path the common case.
181+
if span.from_expansion() {
182+
let expn = span.ctxt().outer_expn_data();
183+
match expn.kind {
184+
// FIXME(@Jarcho): Check that the actual desugaring or std macro is supported by the
185+
// current MSRV. Note that nested expansions need to be handled as well.
186+
ExpnKind::AstPass(_) | ExpnKind::Desugaring(_) => return,
187+
ExpnKind::Macro(..) if expn.macro_def_id.is_some_and(|did| self.std_crates.contains(did.krate)) => {
188+
return;
189+
},
190+
// All other expansions share the target's MSRV.
191+
// FIXME(@Jarcho): What should we do about version dependant macros from external crates?
192+
_ => {},
193+
}
170194
}
171195

172196
if (self.check_in_tests || !is_in_test(cx.tcx, node))

clippy_lints/src/let_if_seq.rs

Lines changed: 76 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use clippy_utils::diagnostics::span_lint_hir_and_then;
22
use clippy_utils::res::MaybeResPath;
3-
use clippy_utils::source::snippet;
3+
use clippy_utils::source::snippet_with_context;
44
use clippy_utils::visitors::is_local_used;
55
use rustc_errors::Applicability;
66
use rustc_hir as hir;
@@ -59,80 +59,88 @@ declare_lint_pass!(LetIfSeq => [USELESS_LET_IF_SEQ]);
5959
impl<'tcx> LateLintPass<'tcx> for LetIfSeq {
6060
fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) {
6161
for [stmt, next] in block.stmts.array_windows::<2>() {
62-
if let hir::StmtKind::Let(local) = stmt.kind
63-
&& let hir::PatKind::Binding(mode, canonical_id, ident, None) = local.pat.kind
64-
&& let hir::StmtKind::Expr(if_) = next.kind
65-
&& let hir::ExprKind::If(cond, then, else_) = if_.kind
66-
&& !is_local_used(cx, cond, canonical_id)
67-
&& let hir::ExprKind::Block(then, _) = then.kind
68-
&& let Some(value) = check_assign(cx, canonical_id, then)
69-
&& !is_local_used(cx, value, canonical_id)
70-
{
71-
let span = stmt.span.to(if_.span);
62+
if let hir::StmtKind::Expr(if_) = next.kind {
63+
check_block_inner(cx, stmt, if_);
64+
}
65+
}
7266

73-
let has_interior_mutability = !cx
74-
.typeck_results()
75-
.node_type(canonical_id)
76-
.is_freeze(cx.tcx, cx.typing_env());
77-
if has_interior_mutability {
78-
return;
79-
}
67+
if let Some(expr) = block.expr
68+
&& let Some(stmt) = block.stmts.last()
69+
{
70+
check_block_inner(cx, stmt, expr);
71+
}
72+
}
73+
}
74+
75+
fn check_block_inner<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx hir::Stmt<'tcx>, if_: &'tcx hir::Expr<'tcx>) {
76+
if let hir::StmtKind::Let(local) = stmt.kind
77+
&& let hir::PatKind::Binding(mode, canonical_id, ident, None) = local.pat.kind
78+
&& let hir::ExprKind::If(cond, then, else_) = if_.kind
79+
&& !is_local_used(cx, cond, canonical_id)
80+
&& let hir::ExprKind::Block(then, _) = then.kind
81+
&& let Some(value) = check_assign(cx, canonical_id, then)
82+
&& !is_local_used(cx, value, canonical_id)
83+
{
84+
let span = stmt.span.to(if_.span);
8085

81-
let (default_multi_stmts, default) = if let Some(else_) = else_ {
82-
if let hir::ExprKind::Block(else_, _) = else_.kind {
83-
if let Some(default) = check_assign(cx, canonical_id, else_) {
84-
(else_.stmts.len() > 1, default)
85-
} else if let Some(default) = local.init {
86-
(true, default)
87-
} else {
88-
continue;
89-
}
90-
} else {
91-
continue;
92-
}
86+
let has_interior_mutability = !cx
87+
.typeck_results()
88+
.node_type(canonical_id)
89+
.is_freeze(cx.tcx, cx.typing_env());
90+
if has_interior_mutability {
91+
return;
92+
}
93+
94+
let (default_multi_stmts, default) = if let Some(else_) = else_ {
95+
if let hir::ExprKind::Block(else_, _) = else_.kind {
96+
if let Some(default) = check_assign(cx, canonical_id, else_) {
97+
(else_.stmts.len() > 1, default)
9398
} else if let Some(default) = local.init {
94-
(false, default)
99+
(true, default)
95100
} else {
96-
continue;
97-
};
101+
return;
102+
}
103+
} else {
104+
return;
105+
}
106+
} else if let Some(default) = local.init {
107+
(false, default)
108+
} else {
109+
return;
110+
};
98111

99-
let mutability = match mode {
100-
BindingMode(_, Mutability::Mut) => "<mut> ",
101-
_ => "",
102-
};
112+
let mutability = match mode {
113+
BindingMode(_, Mutability::Mut) => "<mut> ",
114+
_ => "",
115+
};
103116

104-
// FIXME: this should not suggest `mut` if we can detect that the variable is not
105-
// use mutably after the `if`
117+
// FIXME: this should not suggest `mut` if we can detect that the variable is not
118+
// use mutably after the `if`
106119

107-
let sug = format!(
108-
"let {mutability}{name} = if {cond} {{{then} {value} }} else {{{else} {default} }};",
109-
name=ident.name,
110-
cond=snippet(cx, cond.span, "_"),
111-
then=if then.stmts.len() > 1 { " ..;" } else { "" },
112-
else=if default_multi_stmts { " ..;" } else { "" },
113-
value=snippet(cx, value.span, "<value>"),
114-
default=snippet(cx, default.span, "<default>"),
115-
);
116-
span_lint_hir_and_then(
117-
cx,
118-
USELESS_LET_IF_SEQ,
119-
local.hir_id,
120-
span,
121-
"`if _ { .. } else { .. }` is an expression",
122-
|diag| {
123-
diag.span_suggestion(
124-
span,
125-
"it is more idiomatic to write",
126-
sug,
127-
Applicability::HasPlaceholders,
128-
);
129-
if !mutability.is_empty() {
130-
diag.note("you might not need `mut` at all");
131-
}
132-
},
133-
);
134-
}
135-
}
120+
let mut applicability = Applicability::HasPlaceholders;
121+
let (cond_snip, _) = snippet_with_context(cx, cond.span, if_.span.ctxt(), "_", &mut applicability);
122+
let (value_snip, _) = snippet_with_context(cx, value.span, if_.span.ctxt(), "<value>", &mut applicability);
123+
let (default_snip, _) =
124+
snippet_with_context(cx, default.span, if_.span.ctxt(), "<default>", &mut applicability);
125+
let sug = format!(
126+
"let {mutability}{name} = if {cond_snip} {{{then} {value_snip} }} else {{{else} {default_snip} }};",
127+
name=ident.name,
128+
then=if then.stmts.len() > 1 { " ..;" } else { "" },
129+
else=if default_multi_stmts { " ..;" } else { "" },
130+
);
131+
span_lint_hir_and_then(
132+
cx,
133+
USELESS_LET_IF_SEQ,
134+
local.hir_id,
135+
span,
136+
"`if _ { .. } else { .. }` is an expression",
137+
|diag| {
138+
diag.span_suggestion(span, "it is more idiomatic to write", sug, applicability);
139+
if !mutability.is_empty() {
140+
diag.note("you might not need `mut` at all");
141+
}
142+
},
143+
);
136144
}
137145
}
138146

0 commit comments

Comments
 (0)