mod needless_borrowed_ref;
mod needless_continue;
mod needless_pass_by_value;
+mod needless_return;
mod needless_update;
mod neg_cmp_op_on_partial_ord;
mod neg_multiply;
&needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE,
&needless_continue::NEEDLESS_CONTINUE,
&needless_pass_by_value::NEEDLESS_PASS_BY_VALUE,
+ &needless_return::NEEDLESS_RETURN,
&needless_update::NEEDLESS_UPDATE,
&neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD,
&neg_multiply::NEG_MULTIPLY,
®ex::INVALID_REGEX,
®ex::TRIVIAL_REGEX,
&repeat_once::REPEAT_ONCE,
- &returns::NEEDLESS_RETURN,
&returns::UNUSED_UNIT,
&serde_api::SERDE_API_MISUSE,
&shadow::SHADOW_REUSE,
store.register_late_pass(|| box redundant_closure_call::RedundantClosureCall);
store.register_early_pass(|| box returns::Return);
store.register_late_pass(|| box let_and_return::LetReturn);
+ store.register_late_pass(|| box needless_return::NeedlessReturn);
store.register_early_pass(|| box collapsible_if::CollapsibleIf);
store.register_early_pass(|| box items_after_statements::ItemsAfterStatements);
store.register_early_pass(|| box precedence::Precedence);
LintId::of(&needless_bool::BOOL_COMPARISON),
LintId::of(&needless_bool::NEEDLESS_BOOL),
LintId::of(&needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE),
+ LintId::of(&needless_return::NEEDLESS_RETURN),
LintId::of(&needless_update::NEEDLESS_UPDATE),
LintId::of(&neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD),
LintId::of(&neg_multiply::NEG_MULTIPLY),
LintId::of(®ex::INVALID_REGEX),
LintId::of(®ex::TRIVIAL_REGEX),
LintId::of(&repeat_once::REPEAT_ONCE),
- LintId::of(&returns::NEEDLESS_RETURN),
LintId::of(&returns::UNUSED_UNIT),
LintId::of(&serde_api::SERDE_API_MISUSE),
LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS),
LintId::of(&misc_early::MIXED_CASE_HEX_LITERALS),
LintId::of(&misc_early::REDUNDANT_PATTERN),
LintId::of(&mut_reference::UNNECESSARY_MUT_PASSED),
+ LintId::of(&needless_return::NEEDLESS_RETURN),
LintId::of(&neg_multiply::NEG_MULTIPLY),
LintId::of(&new_without_default::NEW_WITHOUT_DEFAULT),
LintId::of(&non_expressive_names::JUST_UNDERSCORES_AND_DIGITS),
LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES),
LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES),
LintId::of(®ex::TRIVIAL_REGEX),
- LintId::of(&returns::NEEDLESS_RETURN),
LintId::of(&returns::UNUSED_UNIT),
LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS),
LintId::of(&strings::STRING_LIT_AS_BYTES),
--- /dev/null
+use rustc_lint::{LateLintPass, LateContext};
+use rustc_ast::ast::Attribute;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_errors::Applicability;
+use rustc_hir::intravisit::FnKind;
+use rustc_span::source_map::Span;
+use rustc_middle::lint::in_external_macro;
+use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, HirId, MatchSource, StmtKind};
+
+use crate::utils::{snippet_opt, span_lint_and_sugg, span_lint_and_then};
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for return statements at the end of a block.
+ ///
+ /// **Why is this bad?** Removing the `return` and semicolon will make the code
+ /// more rusty.
+ ///
+ /// **Known problems:** If the computation returning the value borrows a local
+ /// variable, removing the `return` may run afoul of the borrow checker.
+ ///
+ /// **Example:**
+ /// ```rust
+ /// fn foo(x: usize) -> usize {
+ /// return x;
+ /// }
+ /// ```
+ /// simplify to
+ /// ```rust
+ /// fn foo(x: usize) -> usize {
+ /// x
+ /// }
+ /// ```
+ pub NEEDLESS_RETURN,
+ style,
+ "using a return statement like `return expr;` where an expression would suffice"
+}
+
+#[derive(PartialEq, Eq, Copy, Clone)]
+enum RetReplacement {
+ Empty,
+ Block,
+}
+
+declare_lint_pass!(NeedlessReturn => [NEEDLESS_RETURN]);
+
+impl<'tcx> LateLintPass<'tcx> for NeedlessReturn {
+ fn check_fn(&mut self, cx: &LateContext<'tcx>, kind: FnKind<'tcx>, _: &'tcx FnDecl<'tcx>, body: &'tcx Body<'tcx>, _: Span, _: HirId) {
+ match kind {
+ FnKind::Closure(_) => {
+ check_final_expr(cx, &body.value, Some(body.value.span), RetReplacement::Empty)
+ }
+ FnKind::ItemFn(..) | FnKind::Method(..) => {
+ if let ExprKind::Block(ref block, _) = body.value.kind {
+ check_block_return(cx, block)
+ }
+ }
+ }
+ }
+}
+
+fn attr_is_cfg(attr: &Attribute) -> bool {
+ attr.meta_item_list().is_some() && attr.has_name(sym!(cfg))
+}
+
+fn check_block_return(cx: &LateContext<'_>, block: &Block<'_>) {
+ if let Some(expr) = block.expr {
+ check_final_expr(cx, expr, Some(expr.span), RetReplacement::Empty);
+ } else if let Some(stmt) = block.stmts.iter().last() {
+ match stmt.kind {
+ StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => {
+ check_final_expr(cx, expr, Some(stmt.span), RetReplacement::Empty);
+ }
+ _ => (),
+ }
+ }
+}
+
+
+fn check_final_expr(
+ cx: &LateContext<'_>,
+ expr: &Expr<'_>,
+ span: Option<Span>,
+ replacement: RetReplacement,
+) {
+ match expr.kind {
+ // simple return is always "bad"
+ ExprKind::Ret(ref inner) => {
+
+ // allow `#[cfg(a)] return a; #[cfg(b)] return b;`
+ if !expr.attrs.iter().any(attr_is_cfg) {
+ emit_return_lint(
+ cx,
+ span.expect("`else return` is not possible"),
+ inner.as_ref().map(|i| i.span),
+ replacement,
+ );
+ }
+ }
+ // a whole block? check it!
+ ExprKind::Block(ref block, _) => {
+ check_block_return(cx, block);
+ }
+ // a match expr, check all arms
+ // an if/if let expr, check both exprs
+ // note, if without else is going to be a type checking error anyways
+ // (except for unit type functions) so we don't match it
+
+ ExprKind::Match(_, ref arms, source) => {
+ match source {
+ MatchSource::Normal => {
+ for arm in arms.iter() {
+ check_final_expr(cx, &arm.body, Some(arm.body.span), RetReplacement::Block);
+ }
+ }
+ MatchSource::IfDesugar { contains_else_clause: true } | MatchSource::IfLetDesugar { contains_else_clause: true } => {
+ if let ExprKind::Block(ref ifblock, _) = arms[0].body.kind {
+ check_block_return(cx, ifblock);
+ }
+ check_final_expr(cx, arms[1].body, None, RetReplacement::Empty);
+ }
+ _ => ()
+ }
+ }
+ _ => (),
+ }
+}
+
+fn emit_return_lint(cx: &LateContext<'_>, ret_span: Span, inner_span: Option<Span>, replacement: RetReplacement) {
+ match inner_span {
+ Some(inner_span) => {
+ if in_external_macro(cx.tcx.sess, inner_span) || inner_span.from_expansion() {
+ return;
+ }
+
+ span_lint_and_then(cx, NEEDLESS_RETURN, ret_span, "unneeded `return` statement", |diag| {
+ if let Some(snippet) = snippet_opt(cx, inner_span) {
+ diag.span_suggestion(ret_span, "remove `return`", snippet, Applicability::MachineApplicable);
+ }
+ })
+ }
+ None => match replacement {
+ RetReplacement::Empty => {
+ span_lint_and_sugg(
+ cx,
+ NEEDLESS_RETURN,
+ ret_span,
+ "unneeded `return` statement",
+ "remove `return`",
+ String::new(),
+ Applicability::MachineApplicable,
+ );
+ }
+ RetReplacement::Block => {
+ span_lint_and_sugg(
+ cx,
+ NEEDLESS_RETURN,
+ ret_span,
+ "unneeded `return` statement",
+ "replace `return` with an empty block",
+ "{}".to_string(),
+ Applicability::MachineApplicable,
+ );
+ }
+ },
+ }
+}
use rustc_ast::visit::FnKind;
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
-use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Span;
use rustc_span::BytePos;
-use crate::utils::{snippet_opt, span_lint_and_sugg, span_lint_and_then};
-
-declare_clippy_lint! {
- /// **What it does:** Checks for return statements at the end of a block.
- ///
- /// **Why is this bad?** Removing the `return` and semicolon will make the code
- /// more rusty.
- ///
- /// **Known problems:** If the computation returning the value borrows a local
- /// variable, removing the `return` may run afoul of the borrow checker.
- ///
- /// **Example:**
- /// ```rust
- /// fn foo(x: usize) -> usize {
- /// return x;
- /// }
- /// ```
- /// simplify to
- /// ```rust
- /// fn foo(x: usize) -> usize {
- /// x
- /// }
- /// ```
- pub NEEDLESS_RETURN,
- style,
- "using a return statement like `return expr;` where an expression would suffice"
-}
+use crate::utils::{span_lint_and_sugg};
declare_clippy_lint! {
/// **What it does:** Checks for unit (`()`) expressions that can be removed.
"needless unit expression"
}
-#[derive(PartialEq, Eq, Copy, Clone)]
-enum RetReplacement {
- Empty,
- Block,
-}
-declare_lint_pass!(Return => [NEEDLESS_RETURN, UNUSED_UNIT]);
-
-impl Return {
- // Check the final stmt or expr in a block for unnecessary return.
- fn check_block_return(&mut self, cx: &EarlyContext<'_>, block: &ast::Block) {
- if let Some(stmt) = block.stmts.last() {
- match stmt.kind {
- ast::StmtKind::Expr(ref expr) | ast::StmtKind::Semi(ref expr) => {
- self.check_final_expr(cx, expr, Some(stmt.span), RetReplacement::Empty);
- },
- _ => (),
- }
- }
- }
-
- // Check the final expression in a block if it's a return.
- fn check_final_expr(
- &mut self,
- cx: &EarlyContext<'_>,
- expr: &ast::Expr,
- span: Option<Span>,
- replacement: RetReplacement,
- ) {
- match expr.kind {
- // simple return is always "bad"
- ast::ExprKind::Ret(ref inner) => {
- // allow `#[cfg(a)] return a; #[cfg(b)] return b;`
- if !expr.attrs.iter().any(attr_is_cfg) {
- Self::emit_return_lint(
- cx,
- span.expect("`else return` is not possible"),
- inner.as_ref().map(|i| i.span),
- replacement,
- );
- }
- },
- // a whole block? check it!
- ast::ExprKind::Block(ref block, _) => {
- self.check_block_return(cx, block);
- },
- // an if/if let expr, check both exprs
- // note, if without else is going to be a type checking error anyways
- // (except for unit type functions) so we don't match it
- ast::ExprKind::If(_, ref ifblock, Some(ref elsexpr)) => {
- self.check_block_return(cx, ifblock);
- self.check_final_expr(cx, elsexpr, None, RetReplacement::Empty);
- },
- // a match expr, check all arms
- ast::ExprKind::Match(_, ref arms) => {
- for arm in arms {
- self.check_final_expr(cx, &arm.body, Some(arm.body.span), RetReplacement::Block);
- }
- },
- _ => (),
- }
- }
-
- fn emit_return_lint(cx: &EarlyContext<'_>, ret_span: Span, inner_span: Option<Span>, replacement: RetReplacement) {
- match inner_span {
- Some(inner_span) => {
- if in_external_macro(cx.sess(), inner_span) || inner_span.from_expansion() {
- return;
- }
-
- span_lint_and_then(cx, NEEDLESS_RETURN, ret_span, "unneeded `return` statement", |diag| {
- if let Some(snippet) = snippet_opt(cx, inner_span) {
- diag.span_suggestion(ret_span, "remove `return`", snippet, Applicability::MachineApplicable);
- }
- })
- },
- None => match replacement {
- RetReplacement::Empty => {
- span_lint_and_sugg(
- cx,
- NEEDLESS_RETURN,
- ret_span,
- "unneeded `return` statement",
- "remove `return`",
- String::new(),
- Applicability::MachineApplicable,
- );
- },
- RetReplacement::Block => {
- span_lint_and_sugg(
- cx,
- NEEDLESS_RETURN,
- ret_span,
- "unneeded `return` statement",
- "replace `return` with an empty block",
- "{}".to_string(),
- Applicability::MachineApplicable,
- );
- },
- },
- }
- }
-}
+declare_lint_pass!(Return => [UNUSED_UNIT]);
impl EarlyLintPass for Return {
fn check_fn(&mut self, cx: &EarlyContext<'_>, kind: FnKind<'_>, span: Span, _: ast::NodeId) {
- match kind {
- FnKind::Fn(.., Some(block)) => self.check_block_return(cx, block),
- FnKind::Closure(_, body) => self.check_final_expr(cx, body, Some(body.span), RetReplacement::Empty),
- FnKind::Fn(.., None) => {},
- }
+
if_chain! {
if let ast::FnRetTy::Ty(ref ty) = kind.decl().output;
if let ast::TyKind::Tup(ref vals) = ty.kind;
}
}
-fn attr_is_cfg(attr: &ast::Attribute) -> bool {
- attr.meta_item_list().is_some() && attr.has_name(sym!(cfg))
-}
// get the def site
#[must_use]
group: "style",
desc: "using a return statement like `return expr;` where an expression would suffice",
deprecation: None,
- module: "returns",
+ module: "needless_return",
},
Lint {
name: "needless_update",