Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 41 additions & 4 deletions compiler/rustc_lint/src/unused.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use rustc_ast::util::{classify, parser};
use rustc_ast::visit::{self, Visitor};
use rustc_ast::{self as ast, ExprKind, FnRetTy, ForLoop, HasAttrs as _, StmtKind};
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::MultiSpan;
Expand Down Expand Up @@ -64,6 +65,7 @@ enum UnusedDelimsCtx {
FunctionArg,
MethodArg,
AssignedValue,
AssignedValueWithWildcard,
AssignedValueLetElse,
IfCond,
WhileCond,
Expand All @@ -85,9 +87,9 @@ impl From<UnusedDelimsCtx> for &'static str {
match ctx {
UnusedDelimsCtx::FunctionArg => "function argument",
UnusedDelimsCtx::MethodArg => "method argument",
UnusedDelimsCtx::AssignedValue | UnusedDelimsCtx::AssignedValueLetElse => {
"assigned value"
}
UnusedDelimsCtx::AssignedValue
| UnusedDelimsCtx::AssignedValueWithWildcard
| UnusedDelimsCtx::AssignedValueLetElse => "assigned value",
UnusedDelimsCtx::IfCond => "`if` condition",
UnusedDelimsCtx::WhileCond => "`while` condition",
UnusedDelimsCtx::ForIterExpr => "`for` iterator expression",
Expand All @@ -104,6 +106,32 @@ impl From<UnusedDelimsCtx> for &'static str {
}
}

fn expr_contains_wildcard(expr: &ast::Expr) -> bool {
struct WildcardVisitor {
found: bool,
}

impl<'ast> Visitor<'ast> for WildcardVisitor {
fn visit_expr(&mut self, expr: &'ast ast::Expr) {
if self.found {
return;
}
match &expr.kind {
ast::ExprKind::Underscore
| ast::ExprKind::Range(None, None, ast::RangeLimits::HalfOpen) => self.found = true,
ast::ExprKind::Struct(expr) if matches!(expr.rest, ast::StructRest::Rest(_)) => {
self.found = true;
}
_ => visit::walk_expr(self, expr),
}
}
}

let mut visitor = WildcardVisitor { found: false };
visitor.visit_expr(expr);
visitor.found
}

/// Used by both `UnusedParens` and `UnusedBraces` to prevent code duplication.
trait UnusedDelimLint {
const DELIM_STR: &'static str;
Expand Down Expand Up @@ -412,7 +440,15 @@ trait UnusedDelimLint {

Index(_, ref value, _) => (value, UnusedDelimsCtx::IndexExpr, false, None, None, false),

Assign(_, ref value, _) | AssignOp(.., ref value) => {
Assign(ref lhs, ref value, _) => {
let ctx = if expr_contains_wildcard(lhs) {
UnusedDelimsCtx::AssignedValueWithWildcard
} else {
UnusedDelimsCtx::AssignedValue
};
(value, ctx, false, None, None, false)
}
AssignOp(.., ref value) => {
(value, UnusedDelimsCtx::AssignedValue, false, None, None, false)
}
// either function/method call, or something this lint doesn't care about
Expand Down Expand Up @@ -1101,6 +1137,7 @@ impl UnusedDelimLint for UnusedBraces {
&& (ctx != UnusedDelimsCtx::AnonConst
|| (matches!(expr.kind, ast::ExprKind::Lit(_))
&& !expr.span.from_expansion()))
&& ctx != UnusedDelimsCtx::AssignedValueWithWildcard
&& ctx != UnusedDelimsCtx::ClosureBody
&& !cx.sess().source_map().is_multiline(value.span)
&& value.attrs.is_empty()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
//@ run-pass

#![deny(unused_braces)]

use std::cell::Cell;

struct Error;
struct NotCopy;

struct DropCounter<'a>(&'a Cell<usize>);

impl Drop for DropCounter<'_> {
fn drop(&mut self) {
self.0.set(self.0.get() + 1);
}
}

struct Pair<'a> {
first: DropCounter<'a>,
_second: DropCounter<'a>,
}

fn wildcard_assignment_moves_value() {
let e = NotCopy;
_ = { e };
}

fn tuple_wildcard_assignment_moves_value() {
let e = (NotCopy, Error);
(_, Error) = { e };
}

fn main() {
wildcard_assignment_moves_value();
tuple_wildcard_assignment_moves_value();

let drops = Cell::new(0);

let e = (DropCounter(&drops), DropCounter(&drops));
(..) = { e };
assert_eq!(drops.get(), 2);

let e = [DropCounter(&drops), DropCounter(&drops)];
[..] = { e };
assert_eq!(drops.get(), 4);

let e = (Error, DropCounter(&drops));
(Error, ..) = { e };
assert_eq!(drops.get(), 5);

let first;
let e = [DropCounter(&drops), DropCounter(&drops)];
[first, ..] = { e };
assert_eq!(drops.get(), 6);
drop(first);
assert_eq!(drops.get(), 7);

let e = (Error, [DropCounter(&drops), DropCounter(&drops)]);
(Error, [..]) = { e };
assert_eq!(drops.get(), 9);

let e = Pair { first: DropCounter(&drops), _second: DropCounter(&drops) };
Pair { .. } = { e };
assert_eq!(drops.get(), 11);

let first;
let e = Pair { first: DropCounter(&drops), _second: DropCounter(&drops) };
Pair { first, .. } = { e };
assert_eq!(drops.get(), 12);
drop(first);
assert_eq!(drops.get(), 13);
}
Loading