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]`.
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",
}
}
-#[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,
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",
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 {
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) => {
) {
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
// 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)