Skip to content

Commit f80c55d

Browse files
committed
add a test for statics and doc comments
1 parent 42c6492 commit f80c55d

File tree

2 files changed

+26
-2
lines changed

2 files changed

+26
-2
lines changed

clippy_lints/src/unwrap.rs

+15-2
Original file line numberDiff line numberDiff line change
@@ -196,13 +196,25 @@ fn collect_unwrap_info<'tcx>(
196196
}
197197

198198
/// A HIR visitor delegate that checks if a local variable of type `Option<_>` is mutated,
199-
/// unless `Option::as_mut` is called.
199+
/// *except* for if `Option::as_mut` is called.
200+
/// The reason for why we allow that one specifically is that `.as_mut()` cannot change
201+
/// the option to `None`, and that is important because this lint relies on the fact that
202+
/// `is_some` + `unwrap` is equivalent to `if let Some(..) = ..`, which it would not be if
203+
/// the option is changed to None between `is_some` and `unwrap`.
204+
/// (And also `.as_mut()` is a somewhat common method that is still worth linting on.)
200205
struct MutationVisitor<'tcx> {
201206
is_mutated: bool,
202207
local_id: HirId,
203208
tcx: TyCtxt<'tcx>,
204209
}
205210

211+
/// Checks if the parent of the expression pointed at by the given `HirId` is a call to
212+
/// `Option::as_mut`.
213+
///
214+
/// Used by the mutation visitor to specifically allow `.as_mut()` calls.
215+
/// In particular, the `HirId` that the visitor receives is the id of the local expression
216+
/// (i.e. the `x` in `x.as_mut()`), and that is the reason for why we care about its parent
217+
/// expression: that will be where the actual method call is.
206218
fn is_option_as_mut_use(tcx: TyCtxt<'_>, expr_id: HirId) -> bool {
207219
if let Node::Expr(mutating_expr) = tcx.hir().get_parent(expr_id)
208220
&& let ExprKind::MethodCall(path, ..) = mutating_expr.kind
@@ -260,7 +272,8 @@ impl<'a, 'tcx> UnwrappableVariablesVisitor<'a, 'tcx> {
260272
vis.walk_expr(branch);
261273

262274
if delegate.is_mutated {
263-
// if the variable is mutated, we don't know whether it can be unwrapped:
275+
// if the variable is mutated, we don't know whether it can be unwrapped.
276+
// it might have been changed to `None` in between `is_some` + `unwrap`.
264277
continue;
265278
}
266279
self.unwrappables.push(unwrap_info);

tests/ui/checked_unwrap/simple_conditionals.rs

+11
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,17 @@ fn issue11371() {
166166
result.as_mut().unwrap();
167167
//~^ ERROR: this call to `unwrap()` will always panic
168168
}
169+
170+
// This should not lint. Statics are, at the time of writing, not linted on anyway,
171+
// but if at some point they are supported by this lint, it should correctly see that
172+
// `X` is being mutated and not suggest `if let Some(..) = X {}`
173+
static mut X: Option<i32> = Some(123);
174+
unsafe {
175+
if X.is_some() {
176+
X = None;
177+
X.unwrap();
178+
}
179+
}
169180
}
170181

171182
fn check_expect() {

0 commit comments

Comments
 (0)