Skip to content

Commit d427b70

Browse files
committed
Auto merge of #12274 - nyurik:nit-format-impl, r=xFrednet
Minor refactor format-impls Move all linting logic into a single format implementations struct This should help with the future format-args improvements. TODO: do the same with format_args.rs, perhaps in the same PR **NOTE TO REVIEWERS**: use "hide whitespace" in the github diff -- most of the code has shifted, but relatively low number of lines actually modified. changelog: none
2 parents 9a253fa + a595a2c commit d427b70

File tree

1 file changed

+106
-97
lines changed

1 file changed

+106
-97
lines changed

clippy_lints/src/format_impl.rs

Lines changed: 106 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use rustc_hir::{Expr, ExprKind, Impl, ImplItem, ImplItemKind, QPath};
77
use rustc_lint::{LateContext, LateLintPass};
88
use rustc_session::impl_lint_pass;
99
use rustc_span::symbol::kw;
10-
use rustc_span::{sym, Span, Symbol};
10+
use rustc_span::{sym, Symbol};
1111

1212
declare_clippy_lint! {
1313
/// ### What it does
@@ -119,123 +119,132 @@ impl<'tcx> LateLintPass<'tcx> for FormatImpl {
119119
}
120120

121121
fn check_impl_item_post(&mut self, cx: &LateContext<'_>, impl_item: &ImplItem<'_>) {
122-
// Assume no nested Impl of Debug and Display within eachother
122+
// Assume no nested Impl of Debug and Display within each other
123123
if is_format_trait_impl(cx, impl_item).is_some() {
124124
self.format_trait_impl = None;
125125
}
126126
}
127127

128128
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
129-
let Some(format_trait_impl) = self.format_trait_impl else {
130-
return;
131-
};
132-
133-
if format_trait_impl.name == sym::Display {
134-
check_to_string_in_display(cx, expr);
129+
if let Some(format_trait_impl) = self.format_trait_impl {
130+
let linter = FormatImplExpr {
131+
cx,
132+
expr,
133+
format_trait_impl,
134+
};
135+
linter.check_to_string_in_display();
136+
linter.check_self_in_format_args();
137+
linter.check_print_in_format_impl();
135138
}
136-
137-
check_self_in_format_args(cx, expr, format_trait_impl);
138-
check_print_in_format_impl(cx, expr, format_trait_impl);
139139
}
140140
}
141141

142-
fn check_to_string_in_display(cx: &LateContext<'_>, expr: &Expr<'_>) {
143-
if let ExprKind::MethodCall(path, self_arg, ..) = expr.kind
144-
// Get the hir_id of the object we are calling the method on
145-
// Is the method to_string() ?
146-
&& path.ident.name == sym::to_string
147-
// Is the method a part of the ToString trait? (i.e. not to_string() implemented
148-
// separately)
149-
&& let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
150-
&& is_diag_trait_item(cx, expr_def_id, sym::ToString)
151-
// Is the method is called on self
152-
&& let ExprKind::Path(QPath::Resolved(_, path)) = self_arg.kind
153-
&& let [segment] = path.segments
154-
&& segment.ident.name == kw::SelfLower
155-
{
156-
span_lint(
157-
cx,
158-
RECURSIVE_FORMAT_IMPL,
159-
expr.span,
160-
"using `self.to_string` in `fmt::Display` implementation will cause infinite recursion",
161-
);
162-
}
142+
struct FormatImplExpr<'a, 'tcx> {
143+
cx: &'a LateContext<'tcx>,
144+
expr: &'tcx Expr<'tcx>,
145+
format_trait_impl: FormatTraitNames,
163146
}
164147

165-
fn check_self_in_format_args<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, impl_trait: FormatTraitNames) {
166-
// Check each arg in format calls - do we ever use Display on self (directly or via deref)?
167-
if let Some(outer_macro) = root_macro_call_first_node(cx, expr)
168-
&& let macro_def_id = outer_macro.def_id
169-
&& is_format_macro(cx, macro_def_id)
170-
&& let Some(format_args) = find_format_args(cx, expr, outer_macro.expn)
171-
{
172-
for piece in &format_args.template {
173-
if let FormatArgsPiece::Placeholder(placeholder) = piece
174-
&& let trait_name = match placeholder.format_trait {
175-
FormatTrait::Display => sym::Display,
176-
FormatTrait::Debug => sym::Debug,
177-
FormatTrait::LowerExp => sym!(LowerExp),
178-
FormatTrait::UpperExp => sym!(UpperExp),
179-
FormatTrait::Octal => sym!(Octal),
180-
FormatTrait::Pointer => sym::Pointer,
181-
FormatTrait::Binary => sym!(Binary),
182-
FormatTrait::LowerHex => sym!(LowerHex),
183-
FormatTrait::UpperHex => sym!(UpperHex),
148+
impl<'a, 'tcx> FormatImplExpr<'a, 'tcx> {
149+
fn check_to_string_in_display(&self) {
150+
if self.format_trait_impl.name == sym::Display
151+
&& let ExprKind::MethodCall(path, self_arg, ..) = self.expr.kind
152+
// Get the hir_id of the object we are calling the method on
153+
// Is the method to_string() ?
154+
&& path.ident.name == sym::to_string
155+
// Is the method a part of the ToString trait? (i.e. not to_string() implemented
156+
// separately)
157+
&& let Some(expr_def_id) = self.cx.typeck_results().type_dependent_def_id(self.expr.hir_id)
158+
&& is_diag_trait_item(self.cx, expr_def_id, sym::ToString)
159+
// Is the method is called on self
160+
&& let ExprKind::Path(QPath::Resolved(_, path)) = self_arg.kind
161+
&& let [segment] = path.segments
162+
&& segment.ident.name == kw::SelfLower
163+
{
164+
span_lint(
165+
self.cx,
166+
RECURSIVE_FORMAT_IMPL,
167+
self.expr.span,
168+
"using `self.to_string` in `fmt::Display` implementation will cause infinite recursion",
169+
);
170+
}
171+
}
172+
173+
fn check_self_in_format_args(&self) {
174+
// Check each arg in format calls - do we ever use Display on self (directly or via deref)?
175+
if let Some(outer_macro) = root_macro_call_first_node(self.cx, self.expr)
176+
&& let macro_def_id = outer_macro.def_id
177+
&& is_format_macro(self.cx, macro_def_id)
178+
&& let Some(format_args) = find_format_args(self.cx, self.expr, outer_macro.expn)
179+
{
180+
for piece in &format_args.template {
181+
if let FormatArgsPiece::Placeholder(placeholder) = piece
182+
&& let trait_name = match placeholder.format_trait {
183+
FormatTrait::Display => sym::Display,
184+
FormatTrait::Debug => sym::Debug,
185+
FormatTrait::LowerExp => sym!(LowerExp),
186+
FormatTrait::UpperExp => sym!(UpperExp),
187+
FormatTrait::Octal => sym!(Octal),
188+
FormatTrait::Pointer => sym::Pointer,
189+
FormatTrait::Binary => sym!(Binary),
190+
FormatTrait::LowerHex => sym!(LowerHex),
191+
FormatTrait::UpperHex => sym!(UpperHex),
192+
}
193+
&& trait_name == self.format_trait_impl.name
194+
&& let Ok(index) = placeholder.argument.index
195+
&& let Some(arg) = format_args.arguments.all_args().get(index)
196+
&& let Ok(arg_expr) = find_format_arg_expr(self.expr, arg)
197+
{
198+
self.check_format_arg_self(arg_expr);
184199
}
185-
&& trait_name == impl_trait.name
186-
&& let Ok(index) = placeholder.argument.index
187-
&& let Some(arg) = format_args.arguments.all_args().get(index)
188-
&& let Ok(arg_expr) = find_format_arg_expr(expr, arg)
189-
{
190-
check_format_arg_self(cx, expr.span, arg_expr, impl_trait);
191200
}
192201
}
193202
}
194-
}
195203

196-
fn check_format_arg_self(cx: &LateContext<'_>, span: Span, arg: &Expr<'_>, impl_trait: FormatTraitNames) {
197-
// Handle multiple dereferencing of references e.g. &&self
198-
// Handle dereference of &self -> self that is equivalent (i.e. via *self in fmt() impl)
199-
// Since the argument to fmt is itself a reference: &self
200-
let reference = peel_ref_operators(cx, arg);
201-
let map = cx.tcx.hir();
202-
// Is the reference self?
203-
if path_to_local(reference).map(|x| map.name(x)) == Some(kw::SelfLower) {
204-
let FormatTraitNames { name, .. } = impl_trait;
205-
span_lint(
206-
cx,
207-
RECURSIVE_FORMAT_IMPL,
208-
span,
209-
&format!("using `self` as `{name}` in `impl {name}` will cause infinite recursion"),
210-
);
204+
fn check_format_arg_self(&self, arg: &Expr<'_>) {
205+
// Handle multiple dereferencing of references e.g. &&self
206+
// Handle dereference of &self -> self that is equivalent (i.e. via *self in fmt() impl)
207+
// Since the argument to fmt is itself a reference: &self
208+
let reference = peel_ref_operators(self.cx, arg);
209+
let map = self.cx.tcx.hir();
210+
// Is the reference self?
211+
if path_to_local(reference).map(|x| map.name(x)) == Some(kw::SelfLower) {
212+
let FormatTraitNames { name, .. } = self.format_trait_impl;
213+
span_lint(
214+
self.cx,
215+
RECURSIVE_FORMAT_IMPL,
216+
self.expr.span,
217+
&format!("using `self` as `{name}` in `impl {name}` will cause infinite recursion"),
218+
);
219+
}
211220
}
212-
}
213221

214-
fn check_print_in_format_impl(cx: &LateContext<'_>, expr: &Expr<'_>, impl_trait: FormatTraitNames) {
215-
if let Some(macro_call) = root_macro_call_first_node(cx, expr)
216-
&& let Some(name) = cx.tcx.get_diagnostic_name(macro_call.def_id)
217-
{
218-
let replacement = match name {
219-
sym::print_macro | sym::eprint_macro => "write",
220-
sym::println_macro | sym::eprintln_macro => "writeln",
221-
_ => return,
222-
};
222+
fn check_print_in_format_impl(&self) {
223+
if let Some(macro_call) = root_macro_call_first_node(self.cx, self.expr)
224+
&& let Some(name) = self.cx.tcx.get_diagnostic_name(macro_call.def_id)
225+
{
226+
let replacement = match name {
227+
sym::print_macro | sym::eprint_macro => "write",
228+
sym::println_macro | sym::eprintln_macro => "writeln",
229+
_ => return,
230+
};
223231

224-
let name = name.as_str().strip_suffix("_macro").unwrap();
232+
let name = name.as_str().strip_suffix("_macro").unwrap();
225233

226-
span_lint_and_sugg(
227-
cx,
228-
PRINT_IN_FORMAT_IMPL,
229-
macro_call.span,
230-
&format!("use of `{name}!` in `{}` impl", impl_trait.name),
231-
"replace with",
232-
if let Some(formatter_name) = impl_trait.formatter_name {
233-
format!("{replacement}!({formatter_name}, ..)")
234-
} else {
235-
format!("{replacement}!(..)")
236-
},
237-
Applicability::HasPlaceholders,
238-
);
234+
span_lint_and_sugg(
235+
self.cx,
236+
PRINT_IN_FORMAT_IMPL,
237+
macro_call.span,
238+
&format!("use of `{name}!` in `{}` impl", self.format_trait_impl.name),
239+
"replace with",
240+
if let Some(formatter_name) = self.format_trait_impl.formatter_name {
241+
format!("{replacement}!({formatter_name}, ..)")
242+
} else {
243+
format!("{replacement}!(..)")
244+
},
245+
Applicability::HasPlaceholders,
246+
);
247+
}
239248
}
240249
}
241250

0 commit comments

Comments
 (0)