]> git.lizzy.rs Git - rust.git/blobdiff - compiler/rustc_lint/src/unused.rs
Handle let-else initializer edge case errors
[rust.git] / compiler / rustc_lint / src / unused.rs
index 2d2897f3f9899bb1716cc4676e1c763e318d7469..f04ac8dd9426ff4d7b32bc81cf705f56fcb5362f 100644 (file)
@@ -1,24 +1,19 @@
 use crate::Lint;
 use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
 use rustc_ast as ast;
-use rustc_ast::util::parser;
+use rustc_ast::util::{classify, parser};
 use rustc_ast::{ExprKind, StmtKind};
 use rustc_ast_pretty::pprust;
-use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::{pluralize, Applicability};
-use rustc_feature::{AttributeType, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP};
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::DefId;
 use rustc_middle::ty::adjustment;
 use rustc_middle::ty::{self, Ty};
-use rustc_session::lint::builtin::UNUSED_ATTRIBUTES;
 use rustc_span::symbol::Symbol;
 use rustc_span::symbol::{kw, sym};
 use rustc_span::{BytePos, Span, DUMMY_SP};
 
-use tracing::debug;
-
 declare_lint! {
     /// The `unused_must_use` lint detects unused result of a type flagged as
     /// `#[must_use]`.
@@ -308,7 +303,7 @@ fn check_must_use_def(
             descr_post_path: &str,
         ) -> bool {
             for attr in cx.tcx.get_attrs(def_id).iter() {
-                if cx.sess().check_name(attr, sym::must_use) {
+                if attr.has_name(sym::must_use) {
                     cx.struct_span_lint(UNUSED_MUST_USE, span, |lint| {
                         let msg = format!(
                             "unused {}`{}`{} that must be used",
@@ -382,67 +377,12 @@ fn check_stmt(&mut self, cx: &LateContext<'_>, s: &hir::Stmt<'_>) {
     }
 }
 
-#[derive(Copy, Clone)]
-pub struct UnusedAttributes {
-    builtin_attributes: &'static FxHashMap<Symbol, &'static BuiltinAttribute>,
-}
-
-impl UnusedAttributes {
-    pub fn new() -> Self {
-        UnusedAttributes { builtin_attributes: &*BUILTIN_ATTRIBUTE_MAP }
-    }
-}
-
-impl_lint_pass!(UnusedAttributes => [UNUSED_ATTRIBUTES]);
-
-impl<'tcx> LateLintPass<'tcx> for UnusedAttributes {
-    fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &ast::Attribute) {
-        debug!("checking attribute: {:?}", attr);
-
-        if attr.is_doc_comment() {
-            return;
-        }
-
-        let attr_info = attr.ident().and_then(|ident| self.builtin_attributes.get(&ident.name));
-
-        if let Some(&&(name, ty, ..)) = attr_info {
-            if let AttributeType::AssumedUsed = ty {
-                debug!("{:?} is AssumedUsed", name);
-                return;
-            }
-        }
-
-        if !cx.sess().is_attr_used(attr) {
-            debug!("emitting warning for: {:?}", attr);
-            cx.struct_span_lint(UNUSED_ATTRIBUTES, attr.span, |lint| {
-                // Mark as used to avoid duplicate warnings.
-                cx.sess().mark_attr_used(attr);
-                lint.build("unused attribute").emit()
-            });
-            // Is it a builtin attribute that must be used at the crate level?
-            if attr_info.map_or(false, |(_, ty, ..)| ty == &AttributeType::CrateLevel) {
-                cx.struct_span_lint(UNUSED_ATTRIBUTES, attr.span, |lint| {
-                    let msg = match attr.style {
-                        ast::AttrStyle::Outer => {
-                            "crate-level attribute should be an inner attribute: add an exclamation \
-                             mark: `#![foo]`"
-                        }
-                        ast::AttrStyle::Inner => "crate-level attribute should be in the root module",
-                    };
-                    lint.build(msg).emit()
-                });
-            }
-        } else {
-            debug!("Attr was used: {:?}", attr);
-        }
-    }
-}
-
 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
 enum UnusedDelimsCtx {
     FunctionArg,
     MethodArg,
     AssignedValue,
+    AssignedValueLetElse,
     IfCond,
     WhileCond,
     ForIterExpr,
@@ -459,7 +399,9 @@ fn from(ctx: UnusedDelimsCtx) -> &'static str {
         match ctx {
             UnusedDelimsCtx::FunctionArg => "function argument",
             UnusedDelimsCtx::MethodArg => "method argument",
-            UnusedDelimsCtx::AssignedValue => "assigned value",
+            UnusedDelimsCtx::AssignedValue | UnusedDelimsCtx::AssignedValueLetElse => {
+                "assigned value"
+            }
             UnusedDelimsCtx::IfCond => "`if` condition",
             UnusedDelimsCtx::WhileCond => "`while` condition",
             UnusedDelimsCtx::ForIterExpr => "`for` iterator expression",
@@ -502,14 +444,26 @@ fn check_unused_delims_expr(
         right_pos: Option<BytePos>,
     );
 
-    fn is_expr_delims_necessary(inner: &ast::Expr, followed_by_block: bool) -> bool {
+    fn is_expr_delims_necessary(
+        inner: &ast::Expr,
+        followed_by_block: bool,
+        followed_by_else: bool,
+    ) -> bool {
+        if followed_by_else {
+            match inner.kind {
+                ast::ExprKind::Binary(op, ..) if op.node.lazy() => return true,
+                _ if classify::expr_trailing_brace(inner).is_some() => return true,
+                _ => {}
+            }
+        }
+
         // Prevent false-positives in cases like `fn x() -> u8 { ({ 0 } + 1) }`
         let lhs_needs_parens = {
             let mut innermost = inner;
             loop {
                 if let ExprKind::Binary(_, lhs, _rhs) = &innermost.kind {
                     innermost = lhs;
-                    if !rustc_ast::util::classify::expr_requires_semi_to_be_stmt(innermost) {
+                    if !classify::expr_requires_semi_to_be_stmt(innermost) {
                         break true;
                     }
                 } else {
@@ -614,7 +568,8 @@ fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
         let (value, ctx, followed_by_block, left_pos, right_pos) = match e.kind {
             // Do not lint `unused_braces` in `if let` expressions.
             If(ref cond, ref block, _)
-                if !matches!(cond.kind, Let(_, _)) || Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX =>
+                if !matches!(cond.kind, Let(_, _, _))
+                    || Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX =>
             {
                 let left = e.span.lo() + rustc_span::BytePos(2);
                 let right = block.span.lo();
@@ -623,7 +578,8 @@ fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
 
             // Do not lint `unused_braces` in `while let` expressions.
             While(ref cond, ref block, ..)
-                if !matches!(cond.kind, Let(_, _)) || Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX =>
+                if !matches!(cond.kind, Let(_, _, _))
+                    || Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX =>
             {
                 let left = e.span.lo() + rustc_span::BytePos(5);
                 let right = block.span.lo();
@@ -677,15 +633,12 @@ fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
     fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) {
         match s.kind {
             StmtKind::Local(ref local) if Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX => {
-                if let Some(ref value) = local.init {
-                    self.check_unused_delims_expr(
-                        cx,
-                        &value,
-                        UnusedDelimsCtx::AssignedValue,
-                        false,
-                        None,
-                        None,
-                    );
+                if let Some((init, els)) = local.kind.init_else_opt() {
+                    let ctx = match els {
+                        None => UnusedDelimsCtx::AssignedValue,
+                        Some(_) => UnusedDelimsCtx::AssignedValueLetElse,
+                    };
+                    self.check_unused_delims_expr(cx, init, ctx, false, None, None);
                 }
             }
             StmtKind::Expr(ref expr) => {
@@ -761,7 +714,8 @@ fn check_unused_delims_expr(
     ) {
         match value.kind {
             ast::ExprKind::Paren(ref inner) => {
-                if !Self::is_expr_delims_necessary(inner, followed_by_block)
+                let followed_by_else = ctx == UnusedDelimsCtx::AssignedValueLetElse;
+                if !Self::is_expr_delims_necessary(inner, followed_by_block, followed_by_else)
                     && value.attrs.is_empty()
                     && !value.span.from_expansion()
                     && (ctx != UnusedDelimsCtx::LetScrutineeExpr
@@ -774,7 +728,7 @@ fn check_unused_delims_expr(
                     self.emit_unused_delims_expr(cx, value, ctx, left_pos, right_pos)
                 }
             }
-            ast::ExprKind::Let(_, ref expr) => {
+            ast::ExprKind::Let(_, ref expr, _) => {
                 self.check_unused_delims_expr(
                     cx,
                     expr,
@@ -828,7 +782,7 @@ fn check_unused_parens_pat(
 impl EarlyLintPass for UnusedParens {
     fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
         match e.kind {
-            ExprKind::Let(ref pat, _) | ExprKind::ForLoop(ref pat, ..) => {
+            ExprKind::Let(ref pat, _, _) | ExprKind::ForLoop(ref pat, ..) => {
                 self.check_unused_parens_pat(cx, pat, false, false);
             }
             // We ignore parens in cases like `if (((let Some(0) = Some(1))))` because we already
@@ -1000,7 +954,7 @@ fn check_unused_delims_expr(
                 // FIXME(const_generics): handle paths when #67075 is fixed.
                 if let [stmt] = inner.stmts.as_slice() {
                     if let ast::StmtKind::Expr(ref expr) = stmt.kind {
-                        if !Self::is_expr_delims_necessary(expr, followed_by_block)
+                        if !Self::is_expr_delims_necessary(expr, followed_by_block, false)
                             && (ctx != UnusedDelimsCtx::AnonConst
                                 || matches!(expr.kind, ast::ExprKind::Lit(_)))
                             && !cx.sess().source_map().is_multiline(value.span)
@@ -1012,7 +966,7 @@ fn check_unused_delims_expr(
                     }
                 }
             }
-            ast::ExprKind::Let(_, ref expr) => {
+            ast::ExprKind::Let(_, ref expr, _) => {
                 self.check_unused_delims_expr(
                     cx,
                     expr,