|
1 | 1 | use clippy_utils::diagnostics::span_lint_and_sugg; |
2 | | -use clippy_utils::path_res; |
| 2 | +use clippy_utils::{ReturnType, ReturnVisitor, path_res, visit_returns}; |
3 | 3 | use rustc_ast::ast::LitKind; |
4 | 4 | use rustc_errors::Applicability; |
5 | 5 | use rustc_hir::def::Res; |
6 | | -use rustc_hir::intravisit::{FnKind, Visitor}; |
7 | | -use rustc_hir::{Body, Expr, ExprKind, FnDecl, FnRetTy, Lit, MutTy, Mutability, PrimTy, Ty, TyKind, intravisit}; |
| 6 | +use rustc_hir::intravisit::FnKind; |
| 7 | +use rustc_hir::{Body, ExprKind, FnDecl, FnRetTy, Lit, MutTy, Mutability, PrimTy, Ty, TyKind}; |
8 | 8 | use rustc_lint::{LateContext, LateLintPass}; |
9 | 9 | use rustc_session::declare_lint_pass; |
10 | 10 | use rustc_span::Span; |
@@ -67,49 +67,36 @@ fn extract_anonymous_ref<'tcx>(hir_ty: &Ty<'tcx>) -> Option<&'tcx Ty<'tcx>> { |
67 | 67 | Some(ty) |
68 | 68 | } |
69 | 69 |
|
70 | | -fn is_str_literal(expr: &Expr<'_>) -> bool { |
71 | | - matches!( |
72 | | - expr.kind, |
73 | | - ExprKind::Lit(Lit { |
74 | | - node: LitKind::Str(..), |
75 | | - .. |
76 | | - }), |
77 | | - ) |
78 | | -} |
79 | | - |
80 | | -struct FindNonLiteralReturn; |
| 70 | +struct LiteralReturnVisitor; |
81 | 71 |
|
82 | | -impl<'hir> Visitor<'hir> for FindNonLiteralReturn { |
| 72 | +impl ReturnVisitor for LiteralReturnVisitor { |
83 | 73 | type Result = std::ops::ControlFlow<()>; |
84 | | - type NestedFilter = intravisit::nested_filter::None; |
| 74 | + fn visit_return(&mut self, kind: ReturnType<'_>) -> Self::Result { |
| 75 | + let expr = match kind { |
| 76 | + ReturnType::Implicit(e) | ReturnType::Explicit(e) => e, |
| 77 | + ReturnType::UnitReturnExplicit(_) | ReturnType::MissingElseImplicit(_) => { |
| 78 | + panic!("Function which returns `&str` has a unit return!") |
| 79 | + }, |
| 80 | + ReturnType::DivergingImplicit(_) => { |
| 81 | + // If this block is implicitly returning `!`, it can return `&'static str`. |
| 82 | + return Self::Result::Continue(()); |
| 83 | + }, |
| 84 | + }; |
85 | 85 |
|
86 | | - fn visit_expr(&mut self, expr: &'hir Expr<'hir>) -> Self::Result { |
87 | | - if let ExprKind::Ret(Some(ret_val_expr)) = expr.kind |
88 | | - && !is_str_literal(ret_val_expr) |
89 | | - { |
90 | | - Self::Result::Break(()) |
| 86 | + if matches!( |
| 87 | + expr.kind, |
| 88 | + ExprKind::Lit(Lit { |
| 89 | + node: LitKind::Str(..), |
| 90 | + .. |
| 91 | + }) |
| 92 | + ) { |
| 93 | + Self::Result::Continue(()) |
91 | 94 | } else { |
92 | | - intravisit::walk_expr(self, expr) |
| 95 | + Self::Result::Break(()) |
93 | 96 | } |
94 | 97 | } |
95 | 98 | } |
96 | 99 |
|
97 | | -fn check_implicit_returns_static_str(body: &Body<'_>) -> bool { |
98 | | - // TODO: Improve this to the same complexity as the Visitor to catch more implicit return cases. |
99 | | - if let ExprKind::Block(block, _) = body.value.kind |
100 | | - && let Some(implicit_ret) = block.expr |
101 | | - { |
102 | | - return is_str_literal(implicit_ret); |
103 | | - } |
104 | | - |
105 | | - false |
106 | | -} |
107 | | - |
108 | | -fn check_explicit_returns_static_str(expr: &Expr<'_>) -> bool { |
109 | | - let mut visitor = FindNonLiteralReturn; |
110 | | - visitor.visit_expr(expr).is_continue() |
111 | | -} |
112 | | - |
113 | 100 | impl<'tcx> LateLintPass<'tcx> for UnnecessaryLiteralBound { |
114 | 101 | fn check_fn( |
115 | 102 | &mut self, |
@@ -143,7 +130,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryLiteralBound { |
143 | 130 | } |
144 | 131 |
|
145 | 132 | // Check for all return statements returning literals |
146 | | - if check_explicit_returns_static_str(body.value) && check_implicit_returns_static_str(body) { |
| 133 | + if visit_returns(LiteralReturnVisitor, body.value).is_continue() { |
147 | 134 | span_lint_and_sugg( |
148 | 135 | cx, |
149 | 136 | UNNECESSARY_LITERAL_BOUND, |
|
0 commit comments