Auto merge of #10434 - Jarcho:snip_context, r=dswij

Remove `snippet_with_macro_callsite`

`snippet_with_context` is used instead to support nested macro calls.

changelog: None
This commit is contained in:
bors 2023-03-11 12:45:20 +00:00
commit f19db28361
22 changed files with 200 additions and 193 deletions

View file

@ -1,5 +1,5 @@
use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_sugg}; use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_sugg};
use clippy_utils::source::snippet_with_macro_callsite; use clippy_utils::source::snippet_with_context;
use clippy_utils::ty::{has_drop, is_copy}; use clippy_utils::ty::{has_drop, is_copy};
use clippy_utils::{ use clippy_utils::{
any_parent_is_automatically_derived, contains_name, get_parent_expr, is_from_proc_macro, match_def_path, paths, any_parent_is_automatically_derived, contains_name, get_parent_expr, is_from_proc_macro, match_def_path, paths,
@ -160,6 +160,8 @@ impl<'tcx> LateLintPass<'tcx> for Default {
} }
}; };
let init_ctxt = local.span.ctxt();
// find all "later statement"'s where the fields of the binding set as // find all "later statement"'s where the fields of the binding set as
// Default::default() get reassigned, unless the reassignment refers to the original binding // Default::default() get reassigned, unless the reassignment refers to the original binding
let mut first_assign = None; let mut first_assign = None;
@ -169,7 +171,7 @@ impl<'tcx> LateLintPass<'tcx> for Default {
// find out if and which field was set by this `consecutive_statement` // find out if and which field was set by this `consecutive_statement`
if let Some((field_ident, assign_rhs)) = field_reassigned_by_stmt(consecutive_statement, binding_name) { if let Some((field_ident, assign_rhs)) = field_reassigned_by_stmt(consecutive_statement, binding_name) {
// interrupt and cancel lint if assign_rhs references the original binding // interrupt and cancel lint if assign_rhs references the original binding
if contains_name(binding_name, assign_rhs, cx) { if contains_name(binding_name, assign_rhs, cx) || init_ctxt != consecutive_statement.span.ctxt() {
cancel_lint = true; cancel_lint = true;
break; break;
} }
@ -204,11 +206,12 @@ impl<'tcx> LateLintPass<'tcx> for Default {
.iter() .iter()
.all(|field| assigned_fields.iter().any(|(a, _)| a == &field.name)); .all(|field| assigned_fields.iter().any(|(a, _)| a == &field.name));
let mut app = Applicability::Unspecified;
let field_list = assigned_fields let field_list = assigned_fields
.into_iter() .into_iter()
.map(|(field, rhs)| { .map(|(field, rhs)| {
// extract and store the assigned value for help message // extract and store the assigned value for help message
let value_snippet = snippet_with_macro_callsite(cx, rhs.span, ".."); let value_snippet = snippet_with_context(cx, rhs.span, init_ctxt, "..", &mut app).0;
format!("{field}: {value_snippet}") format!("{field}: {value_snippet}")
}) })
.collect::<Vec<String>>() .collect::<Vec<String>>()

View file

@ -1,8 +1,10 @@
use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::eager_or_lazy::switch_to_eager_eval; use clippy_utils::eager_or_lazy::switch_to_eager_eval;
use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet_with_macro_callsite; use clippy_utils::source::snippet_with_context;
use clippy_utils::sugg::Sugg;
use clippy_utils::{contains_return, higher, is_else_clause, is_res_lang_ctor, path_res, peel_blocks}; use clippy_utils::{contains_return, higher, is_else_clause, is_res_lang_ctor, path_res, peel_blocks};
use rustc_errors::Applicability;
use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::LangItem::{OptionNone, OptionSome};
use rustc_hir::{Expr, ExprKind, Stmt, StmtKind}; use rustc_hir::{Expr, ExprKind, Stmt, StmtKind};
use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_lint::{LateContext, LateLintPass, LintContext};
@ -72,21 +74,20 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
return; return;
} }
let ctxt = expr.span.ctxt();
if let Some(higher::If { cond, then, r#else: Some(els) }) = higher::If::hir(expr) if let Some(higher::If { cond, then, r#else: Some(els) }) = higher::If::hir(expr)
&& let ExprKind::Block(then_block, _) = then.kind && let ExprKind::Block(then_block, _) = then.kind
&& let Some(then_expr) = then_block.expr && let Some(then_expr) = then_block.expr
&& let ExprKind::Call(then_call, [then_arg]) = then_expr.kind && let ExprKind::Call(then_call, [then_arg]) = then_expr.kind
&& then_expr.span.ctxt() == ctxt
&& is_res_lang_ctor(cx, path_res(cx, then_call), OptionSome) && is_res_lang_ctor(cx, path_res(cx, then_call), OptionSome)
&& is_res_lang_ctor(cx, path_res(cx, peel_blocks(els)), OptionNone) && is_res_lang_ctor(cx, path_res(cx, peel_blocks(els)), OptionNone)
&& !stmts_contains_early_return(then_block.stmts) && !stmts_contains_early_return(then_block.stmts)
{ {
let cond_snip = snippet_with_macro_callsite(cx, cond.span, "[condition]"); let mut app = Applicability::Unspecified;
let cond_snip = if matches!(cond.kind, ExprKind::Unary(_, _) | ExprKind::Binary(_, _, _)) { let cond_snip = Sugg::hir_with_context(cx, cond, expr.span.ctxt(), "[condition]", &mut app).maybe_par().to_string();
format!("({cond_snip})") let arg_snip = snippet_with_context(cx, then_arg.span, ctxt, "[body]", &mut app).0;
} else {
cond_snip.into_owned()
};
let arg_snip = snippet_with_macro_callsite(cx, then_arg.span, "");
let mut method_body = if then_block.stmts.is_empty() { let mut method_body = if then_block.stmts.is_empty() {
arg_snip.into_owned() arg_snip.into_owned()
} else { } else {

View file

@ -1,15 +1,17 @@
use super::SAME_ITEM_PUSH; use super::SAME_ITEM_PUSH;
use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::path_to_local; use clippy_utils::path_to_local;
use clippy_utils::source::snippet_with_macro_callsite; use clippy_utils::source::snippet_with_context;
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
use if_chain::if_chain; use if_chain::if_chain;
use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::fx::FxHashSet;
use rustc_errors::Applicability;
use rustc_hir::def::{DefKind, Res}; use rustc_hir::def::{DefKind, Res};
use rustc_hir::intravisit::{walk_expr, Visitor}; use rustc_hir::intravisit::{walk_expr, Visitor};
use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, HirId, Mutability, Node, Pat, PatKind, Stmt, StmtKind}; use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, HirId, Mutability, Node, Pat, PatKind, Stmt, StmtKind};
use rustc_lint::LateContext; use rustc_lint::LateContext;
use rustc_span::symbol::sym; use rustc_span::symbol::sym;
use rustc_span::SyntaxContext;
use std::iter::Iterator; use std::iter::Iterator;
/// Detects for loop pushing the same item into a Vec /// Detects for loop pushing the same item into a Vec
@ -20,9 +22,10 @@ pub(super) fn check<'tcx>(
body: &'tcx Expr<'_>, body: &'tcx Expr<'_>,
_: &'tcx Expr<'_>, _: &'tcx Expr<'_>,
) { ) {
fn emit_lint(cx: &LateContext<'_>, vec: &Expr<'_>, pushed_item: &Expr<'_>) { fn emit_lint(cx: &LateContext<'_>, vec: &Expr<'_>, pushed_item: &Expr<'_>, ctxt: SyntaxContext) {
let vec_str = snippet_with_macro_callsite(cx, vec.span, ""); let mut app = Applicability::Unspecified;
let item_str = snippet_with_macro_callsite(cx, pushed_item.span, ""); let vec_str = snippet_with_context(cx, vec.span, ctxt, "", &mut app).0;
let item_str = snippet_with_context(cx, pushed_item.span, ctxt, "", &mut app).0;
span_lint_and_help( span_lint_and_help(
cx, cx,
@ -43,7 +46,7 @@ pub(super) fn check<'tcx>(
walk_expr(&mut same_item_push_visitor, body); walk_expr(&mut same_item_push_visitor, body);
if_chain! { if_chain! {
if same_item_push_visitor.should_lint(); if same_item_push_visitor.should_lint();
if let Some((vec, pushed_item)) = same_item_push_visitor.vec_push; if let Some((vec, pushed_item, ctxt)) = same_item_push_visitor.vec_push;
let vec_ty = cx.typeck_results().expr_ty(vec); let vec_ty = cx.typeck_results().expr_ty(vec);
let ty = vec_ty.walk().nth(1).unwrap().expect_ty(); let ty = vec_ty.walk().nth(1).unwrap().expect_ty();
if cx if cx
@ -69,11 +72,11 @@ pub(super) fn check<'tcx>(
then { then {
match init.kind { match init.kind {
// immutable bindings that are initialized with literal // immutable bindings that are initialized with literal
ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item), ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item, ctxt),
// immutable bindings that are initialized with constant // immutable bindings that are initialized with constant
ExprKind::Path(ref path) => { ExprKind::Path(ref path) => {
if let Res::Def(DefKind::Const, ..) = cx.qpath_res(path, init.hir_id) { if let Res::Def(DefKind::Const, ..) = cx.qpath_res(path, init.hir_id) {
emit_lint(cx, vec, pushed_item); emit_lint(cx, vec, pushed_item, ctxt);
} }
} }
_ => {}, _ => {},
@ -82,11 +85,11 @@ pub(super) fn check<'tcx>(
} }
}, },
// constant // constant
Res::Def(DefKind::Const, ..) => emit_lint(cx, vec, pushed_item), Res::Def(DefKind::Const, ..) => emit_lint(cx, vec, pushed_item, ctxt),
_ => {}, _ => {},
} }
}, },
ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item), ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item, ctxt),
_ => {}, _ => {},
} }
} }
@ -98,7 +101,7 @@ struct SameItemPushVisitor<'a, 'tcx> {
non_deterministic_expr: bool, non_deterministic_expr: bool,
multiple_pushes: bool, multiple_pushes: bool,
// this field holds the last vec push operation visited, which should be the only push seen // this field holds the last vec push operation visited, which should be the only push seen
vec_push: Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)>, vec_push: Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>, SyntaxContext)>,
cx: &'a LateContext<'tcx>, cx: &'a LateContext<'tcx>,
used_locals: FxHashSet<HirId>, used_locals: FxHashSet<HirId>,
} }
@ -118,7 +121,7 @@ impl<'a, 'tcx> SameItemPushVisitor<'a, 'tcx> {
if_chain! { if_chain! {
if !self.non_deterministic_expr; if !self.non_deterministic_expr;
if !self.multiple_pushes; if !self.multiple_pushes;
if let Some((vec, _)) = self.vec_push; if let Some((vec, _, _)) = self.vec_push;
if let Some(hir_id) = path_to_local(vec); if let Some(hir_id) = path_to_local(vec);
then { then {
!self.used_locals.contains(&hir_id) !self.used_locals.contains(&hir_id)
@ -173,7 +176,10 @@ impl<'a, 'tcx> Visitor<'tcx> for SameItemPushVisitor<'a, 'tcx> {
// Given some statement, determine if that statement is a push on a Vec. If it is, return // Given some statement, determine if that statement is a push on a Vec. If it is, return
// the Vec being pushed into and the item being pushed // the Vec being pushed into and the item being pushed
fn get_vec_push<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> { fn get_vec_push<'tcx>(
cx: &LateContext<'tcx>,
stmt: &'tcx Stmt<'_>,
) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>, SyntaxContext)> {
if_chain! { if_chain! {
// Extract method being called // Extract method being called
if let StmtKind::Semi(semi_stmt) = &stmt.kind; if let StmtKind::Semi(semi_stmt) = &stmt.kind;
@ -184,7 +190,7 @@ fn get_vec_push<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) -> Option<(&
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_expr), sym::Vec); if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_expr), sym::Vec);
if path.ident.name.as_str() == "push"; if path.ident.name.as_str() == "push";
then { then {
return Some((self_expr, pushed_item)) return Some((self_expr, pushed_item, semi_stmt.span.ctxt()))
} }
} }
None None

View file

@ -32,14 +32,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, scrutinee:
let reindented_or_body = let reindented_or_body =
reindent_multiline(or_body_snippet.into(), true, Some(indent)); reindent_multiline(or_body_snippet.into(), true, Some(indent));
let suggestion = if scrutinee.span.from_expansion() { let mut app = Applicability::MachineApplicable;
// we don't want parentheses around macro, e.g. `(some_macro!()).unwrap_or(0)` let suggestion = sugg::Sugg::hir_with_context(cx, scrutinee, expr.span.ctxt(), "..", &mut app).maybe_par();
sugg::Sugg::hir_with_macro_callsite(cx, scrutinee, "..")
}
else {
sugg::Sugg::hir(cx, scrutinee, "..").maybe_par()
};
span_lint_and_sugg( span_lint_and_sugg(
cx, cx,
MANUAL_UNWRAP_OR, expr.span, MANUAL_UNWRAP_OR, expr.span,
@ -48,7 +42,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, scrutinee:
format!( format!(
"{suggestion}.unwrap_or({reindented_or_body})", "{suggestion}.unwrap_or({reindented_or_body})",
), ),
Applicability::MachineApplicable, app,
); );
} }
} }

View file

@ -10,9 +10,9 @@ use rustc_middle::ty;
use super::MATCH_BOOL; use super::MATCH_BOOL;
pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) { pub(crate) fn check(cx: &LateContext<'_>, scrutinee: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
// Type of expression is `bool`. // Type of expression is `bool`.
if *cx.typeck_results().expr_ty(ex).kind() == ty::Bool { if *cx.typeck_results().expr_ty(scrutinee).kind() == ty::Bool {
span_lint_and_then( span_lint_and_then(
cx, cx,
MATCH_BOOL, MATCH_BOOL,
@ -36,24 +36,26 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr:
}; };
if let Some((true_expr, false_expr)) = exprs { if let Some((true_expr, false_expr)) = exprs {
let mut app = Applicability::HasPlaceholders;
let ctxt = expr.span.ctxt();
let sugg = match (is_unit_expr(true_expr), is_unit_expr(false_expr)) { let sugg = match (is_unit_expr(true_expr), is_unit_expr(false_expr)) {
(false, false) => Some(format!( (false, false) => Some(format!(
"if {} {} else {}", "if {} {} else {}",
snippet(cx, ex.span, "b"), snippet(cx, scrutinee.span, "b"),
expr_block(cx, true_expr, None, "..", Some(expr.span)), expr_block(cx, true_expr, ctxt, "..", Some(expr.span), &mut app),
expr_block(cx, false_expr, None, "..", Some(expr.span)) expr_block(cx, false_expr, ctxt, "..", Some(expr.span), &mut app)
)), )),
(false, true) => Some(format!( (false, true) => Some(format!(
"if {} {}", "if {} {}",
snippet(cx, ex.span, "b"), snippet(cx, scrutinee.span, "b"),
expr_block(cx, true_expr, None, "..", Some(expr.span)) expr_block(cx, true_expr, ctxt, "..", Some(expr.span), &mut app)
)), )),
(true, false) => { (true, false) => {
let test = Sugg::hir(cx, ex, ".."); let test = Sugg::hir(cx, scrutinee, "..");
Some(format!( Some(format!(
"if {} {}", "if {} {}",
!test, !test,
expr_block(cx, false_expr, None, "..", Some(expr.span)) expr_block(cx, false_expr, ctxt, "..", Some(expr.span), &mut app)
)) ))
}, },
(true, true) => None, (true, true) => None,

View file

@ -1,13 +1,14 @@
use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
use clippy_utils::source::snippet; use clippy_utils::source::{snippet, walk_span_to_context};
use clippy_utils::sugg::Sugg; use clippy_utils::sugg::Sugg;
use core::iter::once; use core::iter::once;
use rustc_errors::Applicability;
use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Pat, PatKind}; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Pat, PatKind};
use rustc_lint::LateContext; use rustc_lint::LateContext;
use super::MATCH_REF_PATS; use super::MATCH_REF_PATS;
pub(crate) fn check<'a, 'b, I>(cx: &LateContext<'_>, ex: &Expr<'_>, pats: I, expr: &Expr<'_>) pub(crate) fn check<'a, 'b, I>(cx: &LateContext<'_>, scrutinee: &Expr<'_>, pats: I, expr: &Expr<'_>)
where where
'b: 'a, 'b: 'a,
I: Clone + Iterator<Item = &'a Pat<'b>>, I: Clone + Iterator<Item = &'a Pat<'b>>,
@ -17,13 +18,28 @@ where
} }
let (first_sugg, msg, title); let (first_sugg, msg, title);
let span = ex.span.source_callsite(); let ctxt = expr.span.ctxt();
if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = ex.kind { let mut app = Applicability::Unspecified;
first_sugg = once((span, Sugg::hir_with_macro_callsite(cx, inner, "..").to_string())); if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = scrutinee.kind {
if scrutinee.span.ctxt() != ctxt {
return;
}
first_sugg = once((
scrutinee.span,
Sugg::hir_with_context(cx, inner, ctxt, "..", &mut app).to_string(),
));
msg = "try"; msg = "try";
title = "you don't need to add `&` to both the expression and the patterns"; title = "you don't need to add `&` to both the expression and the patterns";
} else { } else {
first_sugg = once((span, Sugg::hir_with_macro_callsite(cx, ex, "..").deref().to_string())); let Some(span) = walk_span_to_context(scrutinee.span, ctxt) else {
return;
};
first_sugg = once((
span,
Sugg::hir_with_context(cx, scrutinee, ctxt, "..", &mut app)
.deref()
.to_string(),
));
msg = "instead of prefixing all patterns with `&`, you can dereference the expression"; msg = "instead of prefixing all patterns with `&`, you can dereference the expression";
title = "you don't need to add `&` to all patterns"; title = "you don't need to add `&` to all patterns";
} }

View file

@ -1,7 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::macros::HirNode; use clippy_utils::macros::HirNode;
use clippy_utils::source::{indent_of, snippet, snippet_block, snippet_with_applicability}; use clippy_utils::source::{indent_of, snippet, snippet_block_with_context, snippet_with_applicability};
use clippy_utils::sugg::Sugg;
use clippy_utils::{get_parent_expr, is_refutable, peel_blocks}; use clippy_utils::{get_parent_expr, is_refutable, peel_blocks};
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{Arm, Expr, ExprKind, Node, PatKind}; use rustc_hir::{Arm, Expr, ExprKind, Node, PatKind};
@ -24,21 +23,25 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e
let matched_vars = ex.span; let matched_vars = ex.span;
let bind_names = arms[0].pat.span; let bind_names = arms[0].pat.span;
let match_body = peel_blocks(arms[0].body); let match_body = peel_blocks(arms[0].body);
let mut snippet_body = if match_body.span.from_expansion() { let mut app = Applicability::MaybeIncorrect;
Sugg::hir_with_macro_callsite(cx, match_body, "..").to_string() let (snippet_body, from_macro) = snippet_block_with_context(
} else { cx,
snippet_block(cx, match_body.span, "..", Some(expr.span)).to_string() match_body.span,
}; arms[0].span.ctxt(),
"..",
Some(expr.span),
&mut app,
);
let mut snippet_body = snippet_body.to_string();
// Do we need to add ';' to suggestion ? // Do we need to add ';' to suggestion ?
if let ExprKind::Block(block, _) = match_body.kind { if matches!(match_body.kind, ExprKind::Block(..)) {
// macro + expr_ty(body) == () // macro + expr_ty(body) == ()
if block.span.from_expansion() && cx.typeck_results().expr_ty(match_body).is_unit() { if from_macro && cx.typeck_results().expr_ty(match_body).is_unit() {
snippet_body.push(';'); snippet_body.push(';');
} }
} }
let mut applicability = Applicability::MaybeIncorrect;
match arms[0].pat.kind { match arms[0].pat.kind {
PatKind::Binding(..) | PatKind::Tuple(_, _) | PatKind::Struct(..) => { PatKind::Binding(..) | PatKind::Tuple(_, _) | PatKind::Struct(..) => {
let (target_span, sugg) = match opt_parent_assign_span(cx, ex) { let (target_span, sugg) = match opt_parent_assign_span(cx, ex) {
@ -48,7 +51,7 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e
(ex, expr), (ex, expr),
(bind_names, matched_vars), (bind_names, matched_vars),
&snippet_body, &snippet_body,
&mut applicability, &mut app,
Some(span), Some(span),
true, true,
); );
@ -60,7 +63,7 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e
"this assignment could be simplified", "this assignment could be simplified",
"consider removing the `match` expression", "consider removing the `match` expression",
sugg, sugg,
applicability, app,
); );
return; return;
@ -69,10 +72,10 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e
span, span,
format!( format!(
"let {} = {};\n{}let {} = {snippet_body};", "let {} = {};\n{}let {} = {snippet_body};",
snippet_with_applicability(cx, bind_names, "..", &mut applicability), snippet_with_applicability(cx, bind_names, "..", &mut app),
snippet_with_applicability(cx, matched_vars, "..", &mut applicability), snippet_with_applicability(cx, matched_vars, "..", &mut app),
" ".repeat(indent_of(cx, expr.span).unwrap_or(0)), " ".repeat(indent_of(cx, expr.span).unwrap_or(0)),
snippet_with_applicability(cx, pat_span, "..", &mut applicability) snippet_with_applicability(cx, pat_span, "..", &mut app)
), ),
), ),
None => { None => {
@ -81,7 +84,7 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e
(ex, expr), (ex, expr),
(bind_names, matched_vars), (bind_names, matched_vars),
&snippet_body, &snippet_body,
&mut applicability, &mut app,
None, None,
true, true,
); );
@ -96,7 +99,7 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e
"this match could be written as a `let` statement", "this match could be written as a `let` statement",
"consider using a `let` statement", "consider using a `let` statement",
sugg, sugg,
applicability, app,
); );
}, },
PatKind::Wild => { PatKind::Wild => {
@ -106,7 +109,7 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e
(ex, expr), (ex, expr),
(bind_names, matched_vars), (bind_names, matched_vars),
&snippet_body, &snippet_body,
&mut applicability, &mut app,
None, None,
false, false,
); );
@ -118,7 +121,7 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e
"this match could be replaced by its scrutinee and body", "this match could be replaced by its scrutinee and body",
"consider using the scrutinee and body instead", "consider using the scrutinee and body instead",
sugg, sugg,
applicability, app,
); );
} else { } else {
span_lint_and_sugg( span_lint_and_sugg(

View file

@ -1,6 +1,6 @@
use super::REDUNDANT_PATTERN_MATCHING; use super::REDUNDANT_PATTERN_MATCHING;
use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet; use clippy_utils::source::{snippet, walk_span_to_context};
use clippy_utils::sugg::Sugg; use clippy_utils::sugg::Sugg;
use clippy_utils::ty::{is_type_diagnostic_item, needs_ordered_drop}; use clippy_utils::ty::{is_type_diagnostic_item, needs_ordered_drop};
use clippy_utils::visitors::any_temporaries_need_ordered_drop; use clippy_utils::visitors::any_temporaries_need_ordered_drop;
@ -150,22 +150,25 @@ fn find_sugg_for_if_let<'tcx>(
// if/while let ... = ... { ... } // if/while let ... = ... { ... }
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^ // ^^^^^^^^^^^^^^^^^^^^^^^^^^^
let expr_span = expr.span; let expr_span = expr.span;
let ctxt = expr.span.ctxt();
// if/while let ... = ... { ... } // if/while let ... = ... { ... }
// ^^^ // ^^^
let op_span = result_expr.span.source_callsite(); let Some(res_span) = walk_span_to_context(result_expr.span.source_callsite(), ctxt) else {
return;
};
// if/while let ... = ... { ... } // if/while let ... = ... { ... }
// ^^^^^^^^^^^^^^^^^^^ // ^^^^^^^^^^^^^^^^^^^^^^
let span = expr_span.until(op_span.shrink_to_hi()); let span = expr_span.until(res_span.shrink_to_hi());
let app = if needs_drop { let mut app = if needs_drop {
Applicability::MaybeIncorrect Applicability::MaybeIncorrect
} else { } else {
Applicability::MachineApplicable Applicability::MachineApplicable
}; };
let sugg = Sugg::hir_with_macro_callsite(cx, result_expr, "_") let sugg = Sugg::hir_with_context(cx, result_expr, ctxt, "_", &mut app)
.maybe_par() .maybe_par()
.to_string(); .to_string();

View file

@ -67,8 +67,10 @@ fn report_single_pattern(
els: Option<&Expr<'_>>, els: Option<&Expr<'_>>,
) { ) {
let lint = if els.is_some() { SINGLE_MATCH_ELSE } else { SINGLE_MATCH }; let lint = if els.is_some() { SINGLE_MATCH_ELSE } else { SINGLE_MATCH };
let ctxt = expr.span.ctxt();
let mut app = Applicability::HasPlaceholders;
let els_str = els.map_or(String::new(), |els| { let els_str = els.map_or(String::new(), |els| {
format!(" else {}", expr_block(cx, els, None, "..", Some(expr.span))) format!(" else {}", expr_block(cx, els, ctxt, "..", Some(expr.span), &mut app))
}); });
let (pat, pat_ref_count) = peel_hir_pat_refs(arms[0].pat); let (pat, pat_ref_count) = peel_hir_pat_refs(arms[0].pat);
@ -103,7 +105,7 @@ fn report_single_pattern(
// PartialEq for different reference counts may not exist. // PartialEq for different reference counts may not exist.
"&".repeat(ref_count_diff), "&".repeat(ref_count_diff),
snippet(cx, arms[0].pat.span, ".."), snippet(cx, arms[0].pat.span, ".."),
expr_block(cx, arms[0].body, None, "..", Some(expr.span)), expr_block(cx, arms[0].body, ctxt, "..", Some(expr.span), &mut app),
); );
(msg, sugg) (msg, sugg)
} else { } else {
@ -112,21 +114,13 @@ fn report_single_pattern(
"if let {} = {} {}{els_str}", "if let {} = {} {}{els_str}",
snippet(cx, arms[0].pat.span, ".."), snippet(cx, arms[0].pat.span, ".."),
snippet(cx, ex.span, ".."), snippet(cx, ex.span, ".."),
expr_block(cx, arms[0].body, None, "..", Some(expr.span)), expr_block(cx, arms[0].body, ctxt, "..", Some(expr.span), &mut app),
); );
(msg, sugg) (msg, sugg)
} }
}; };
span_lint_and_sugg( span_lint_and_sugg(cx, lint, expr.span, msg, "try this", sugg, app);
cx,
lint,
expr.span,
msg,
"try this",
sugg,
Applicability::HasPlaceholders,
);
} }
fn check_opt_like<'a>( fn check_opt_like<'a>(

View file

@ -1,6 +1,6 @@
use super::{contains_return, BIND_INSTEAD_OF_MAP}; use super::{contains_return, BIND_INSTEAD_OF_MAP};
use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_and_sugg, span_lint_and_then};
use clippy_utils::source::{snippet, snippet_with_macro_callsite}; use clippy_utils::source::{snippet, snippet_with_context};
use clippy_utils::{peel_blocks, visitors::find_all_ret_expressions}; use clippy_utils::{peel_blocks, visitors::find_all_ret_expressions};
use if_chain::if_chain; use if_chain::if_chain;
use rustc_errors::Applicability; use rustc_errors::Applicability;
@ -76,11 +76,8 @@ pub(crate) trait BindInsteadOfMap {
if !contains_return(inner_expr); if !contains_return(inner_expr);
if let Some(msg) = Self::lint_msg(cx); if let Some(msg) = Self::lint_msg(cx);
then { then {
let some_inner_snip = if inner_expr.span.from_expansion() { let mut app = Applicability::MachineApplicable;
snippet_with_macro_callsite(cx, inner_expr.span, "_") let some_inner_snip = snippet_with_context(cx, inner_expr.span, closure_expr.span.ctxt(), "_", &mut app).0;
} else {
snippet(cx, inner_expr.span, "_")
};
let closure_args_snip = snippet(cx, closure_args_span, ".."); let closure_args_snip = snippet(cx, closure_args_span, "..");
let option_snip = snippet(cx, recv.span, ".."); let option_snip = snippet(cx, recv.span, "..");
@ -92,7 +89,7 @@ pub(crate) trait BindInsteadOfMap {
&msg, &msg,
"try this", "try this",
note, note,
Applicability::MachineApplicable, app,
); );
true true
} else { } else {

View file

@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::paths; use clippy_utils::paths;
use clippy_utils::source::snippet_with_macro_callsite; use clippy_utils::source::snippet_with_context;
use clippy_utils::ty::{is_type_diagnostic_item, match_type}; use clippy_utils::ty::{is_type_diagnostic_item, match_type};
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir as hir; use rustc_hir as hir;
@ -33,7 +33,9 @@ pub(super) fn check(
return; return;
}; };
let snippet = snippet_with_macro_callsite(cx, receiver.span, ".."); // Sometimes unnecessary ::<_> after Rc/Arc/Weak
let mut app = Applicability::Unspecified;
let snippet = snippet_with_context(cx, receiver.span, expr.span.ctxt(), "..", &mut app).0;
span_lint_and_sugg( span_lint_and_sugg(
cx, cx,
@ -42,7 +44,7 @@ pub(super) fn check(
"using `.clone()` on a ref-counted pointer", "using `.clone()` on a ref-counted pointer",
"try this", "try this",
format!("{caller_type}::<{}>::clone(&{snippet})", subst.type_at(0)), format!("{caller_type}::<{}>::clone(&{snippet})", subst.type_at(0)),
Applicability::Unspecified, // Sometimes unnecessary ::<_> after Rc/Arc/Weak app,
); );
} }
} }

View file

@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::eager_or_lazy::switch_to_lazy_eval; use clippy_utils::eager_or_lazy::switch_to_lazy_eval;
use clippy_utils::source::{snippet, snippet_with_macro_callsite}; use clippy_utils::source::snippet_with_context;
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
use clippy_utils::{contains_return, is_trait_item, last_path_segment}; use clippy_utils::{contains_return, is_trait_item, last_path_segment};
use if_chain::if_chain; use if_chain::if_chain;
@ -9,7 +9,6 @@ use rustc_hir as hir;
use rustc_lint::LateContext; use rustc_lint::LateContext;
use rustc_span::source_map::Span; use rustc_span::source_map::Span;
use rustc_span::symbol::{kw, sym, Symbol}; use rustc_span::symbol::{kw, sym, Symbol};
use std::borrow::Cow;
use super::OR_FUN_CALL; use super::OR_FUN_CALL;
@ -111,37 +110,24 @@ pub(super) fn check<'tcx>(
if poss.contains(&name); if poss.contains(&name);
then { then {
let ctxt = span.ctxt();
let mut app = Applicability::HasPlaceholders;
let sugg = { let sugg = {
let (snippet_span, use_lambda) = match (fn_has_arguments, fun_span) { let (snippet_span, use_lambda) = match (fn_has_arguments, fun_span) {
(false, Some(fun_span)) => (fun_span, false), (false, Some(fun_span)) => (fun_span, false),
_ => (arg.span, true), _ => (arg.span, true),
}; };
let format_span = |span: Span| { let snip = snippet_with_context(cx, snippet_span, ctxt, "..", &mut app).0;
let not_macro_argument_snippet = snippet_with_macro_callsite(cx, span, "..");
let snip = if not_macro_argument_snippet == "vec![]" {
let macro_expanded_snipped = snippet(cx, snippet_span, "..");
match macro_expanded_snipped.strip_prefix("$crate::vec::") {
Some(stripped) => Cow::Owned(stripped.to_owned()),
None => macro_expanded_snipped,
}
} else {
not_macro_argument_snippet
};
snip.to_string()
};
let snip = format_span(snippet_span);
let snip = if use_lambda { let snip = if use_lambda {
let l_arg = if fn_has_arguments { "_" } else { "" }; let l_arg = if fn_has_arguments { "_" } else { "" };
format!("|{l_arg}| {snip}") format!("|{l_arg}| {snip}")
} else { } else {
snip snip.into_owned()
}; };
if let Some(f) = second_arg { if let Some(f) = second_arg {
let f = format_span(f.span); let f = snippet_with_context(cx, f.span, ctxt, "..", &mut app).0;
format!("{snip}, {f}") format!("{snip}, {f}")
} else { } else {
snip snip
@ -155,7 +141,7 @@ pub(super) fn check<'tcx>(
&format!("use of `{name}` followed by a function call"), &format!("use of `{name}` followed by a function call"),
"try this", "try this",
format!("{name}_{suffix}({sugg})"), format!("{name}_{suffix}({sugg})"),
Applicability::HasPlaceholders, app,
); );
} }
} }

View file

@ -1,5 +1,5 @@
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_hir_and_then};
use clippy_utils::source::{snippet, snippet_opt}; use clippy_utils::source::{snippet, snippet_opt, snippet_with_context};
use if_chain::if_chain; use if_chain::if_chain;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::intravisit::FnKind; use rustc_hir::intravisit::FnKind;
@ -181,20 +181,17 @@ impl<'tcx> LateLintPass<'tcx> for LintPass {
if let PatKind::Binding(BindingAnnotation(ByRef::Yes, mutabl), .., name, None) = local.pat.kind; if let PatKind::Binding(BindingAnnotation(ByRef::Yes, mutabl), .., name, None) = local.pat.kind;
if let Some(init) = local.init; if let Some(init) = local.init;
then { then {
// use the macro callsite when the init span (but not the whole local span) let ctxt = local.span.ctxt();
// comes from an expansion like `vec![1, 2, 3]` in `let ref _ = vec![1, 2, 3];` let mut app = Applicability::MachineApplicable;
let sugg_init = if init.span.from_expansion() && !local.span.from_expansion() { let sugg_init = Sugg::hir_with_context(cx, init, ctxt, "..", &mut app);
Sugg::hir_with_macro_callsite(cx, init, "..")
} else {
Sugg::hir(cx, init, "..")
};
let (mutopt, initref) = if mutabl == Mutability::Mut { let (mutopt, initref) = if mutabl == Mutability::Mut {
("mut ", sugg_init.mut_addr()) ("mut ", sugg_init.mut_addr())
} else { } else {
("", sugg_init.addr()) ("", sugg_init.addr())
}; };
let tyopt = if let Some(ty) = local.ty { let tyopt = if let Some(ty) = local.ty {
format!(": &{mutopt}{ty}", ty=snippet(cx, ty.span, "..")) let ty_snip = snippet_with_context(cx, ty.span, ctxt, "_", &mut app).0;
format!(": &{mutopt}{ty_snip}")
} else { } else {
String::new() String::new()
}; };
@ -212,7 +209,7 @@ impl<'tcx> LateLintPass<'tcx> for LintPass {
"let {name}{tyopt} = {initref};", "let {name}{tyopt} = {initref};",
name=snippet(cx, name.span, ".."), name=snippet(cx, name.span, ".."),
), ),
Applicability::MachineApplicable, app,
); );
} }
); );

View file

@ -340,18 +340,11 @@ fn suggest_bool_comparison<'a, 'tcx>(
cx: &LateContext<'tcx>, cx: &LateContext<'tcx>,
e: &'tcx Expr<'_>, e: &'tcx Expr<'_>,
expr: &Expr<'_>, expr: &Expr<'_>,
mut applicability: Applicability, mut app: Applicability,
message: &str, message: &str,
conv_hint: impl FnOnce(Sugg<'a>) -> Sugg<'a>, conv_hint: impl FnOnce(Sugg<'a>) -> Sugg<'a>,
) { ) {
let hint = if expr.span.from_expansion() { let hint = Sugg::hir_with_context(cx, expr, e.span.ctxt(), "..", &mut app);
if applicability != Applicability::Unspecified {
applicability = Applicability::MaybeIncorrect;
}
Sugg::hir_with_macro_callsite(cx, expr, "..")
} else {
Sugg::hir_with_applicability(cx, expr, "..", &mut applicability)
};
span_lint_and_sugg( span_lint_and_sugg(
cx, cx,
BOOL_COMPARISON, BOOL_COMPARISON,
@ -359,7 +352,7 @@ fn suggest_bool_comparison<'a, 'tcx>(
message, message,
"try simplifying it as shown", "try simplifying it as shown",
conv_hint(hint).to_string(), conv_hint(hint).to_string(),
applicability, app,
); );
} }

View file

@ -12,6 +12,7 @@ use rustc_hir::{
}; };
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::SyntaxContext;
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### What it does
@ -95,10 +96,10 @@ struct OptionOccurrence {
none_expr: String, none_expr: String,
} }
fn format_option_in_sugg(cx: &LateContext<'_>, cond_expr: &Expr<'_>, as_ref: bool, as_mut: bool) -> String { fn format_option_in_sugg(cond_sugg: Sugg<'_>, as_ref: bool, as_mut: bool) -> String {
format!( format!(
"{}{}", "{}{}",
Sugg::hir_with_macro_callsite(cx, cond_expr, "..").maybe_par(), cond_sugg.maybe_par(),
if as_mut { if as_mut {
".as_mut()" ".as_mut()"
} else if as_ref { } else if as_ref {
@ -111,6 +112,7 @@ fn format_option_in_sugg(cx: &LateContext<'_>, cond_expr: &Expr<'_>, as_ref: boo
fn try_get_option_occurrence<'tcx>( fn try_get_option_occurrence<'tcx>(
cx: &LateContext<'tcx>, cx: &LateContext<'tcx>,
ctxt: SyntaxContext,
pat: &Pat<'tcx>, pat: &Pat<'tcx>,
expr: &Expr<'_>, expr: &Expr<'_>,
if_then: &'tcx Expr<'_>, if_then: &'tcx Expr<'_>,
@ -160,11 +162,23 @@ fn try_get_option_occurrence<'tcx>(
} }
} }
let mut app = Applicability::Unspecified;
return Some(OptionOccurrence { return Some(OptionOccurrence {
option: format_option_in_sugg(cx, cond_expr, as_ref, as_mut), option: format_option_in_sugg(
Sugg::hir_with_context(cx, cond_expr, ctxt, "..", &mut app),
as_ref,
as_mut,
),
method_sugg: method_sugg.to_string(), method_sugg: method_sugg.to_string(),
some_expr: format!("|{capture_mut}{capture_name}| {}", Sugg::hir_with_macro_callsite(cx, some_body, "..")), some_expr: format!(
none_expr: format!("{}{}", if method_sugg == "map_or" { "" } else { "|| " }, Sugg::hir_with_macro_callsite(cx, none_body, "..")), "|{capture_mut}{capture_name}| {}",
Sugg::hir_with_context(cx, some_body, ctxt, "..", &mut app),
),
none_expr: format!(
"{}{}",
if method_sugg == "map_or" { "" } else { "|| " },
Sugg::hir_with_context(cx, none_body, ctxt, "..", &mut app),
),
}); });
} }
} }
@ -194,7 +208,7 @@ fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) ->
}) = higher::IfLet::hir(cx, expr) }) = higher::IfLet::hir(cx, expr)
{ {
if !is_else_clause(cx.tcx, expr) { if !is_else_clause(cx.tcx, expr) {
return try_get_option_occurrence(cx, let_pat, let_expr, if_then, if_else); return try_get_option_occurrence(cx, expr.span.ctxt(), let_pat, let_expr, if_then, if_else);
} }
} }
None None
@ -203,7 +217,7 @@ fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) ->
fn detect_option_match<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<OptionOccurrence> { fn detect_option_match<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<OptionOccurrence> {
if let ExprKind::Match(ex, arms, MatchSource::Normal) = expr.kind { if let ExprKind::Match(ex, arms, MatchSource::Normal) = expr.kind {
if let Some((let_pat, if_then, if_else)) = try_convert_match(cx, arms) { if let Some((let_pat, if_then, if_else)) = try_convert_match(cx, arms) {
return try_get_option_occurrence(cx, let_pat, ex, if_then, if_else); return try_get_option_occurrence(cx, expr.span.ctxt(), let_pat, ex, if_then, if_else);
} }
} }
None None

View file

@ -1,7 +1,6 @@
use crate::rustc_lint::LintContext; use crate::rustc_lint::LintContext;
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_macro_callsite; use clippy_utils::source::snippet_with_context;
use clippy_utils::sugg;
use if_chain::if_chain; use if_chain::if_chain;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{Block, ExprKind}; use rustc_hir::{Block, ExprKind};
@ -44,7 +43,8 @@ impl<'tcx> LateLintPass<'tcx> for SemicolonIfNothingReturned {
if let Some(expr) = block.expr; if let Some(expr) = block.expr;
let t_expr = cx.typeck_results().expr_ty(expr); let t_expr = cx.typeck_results().expr_ty(expr);
if t_expr.is_unit(); if t_expr.is_unit();
if let snippet = snippet_with_macro_callsite(cx, expr.span, "}"); let mut app = Applicability::MaybeIncorrect;
if let snippet = snippet_with_context(cx, expr.span, block.span.ctxt(), "}", &mut app).0;
if !snippet.ends_with('}') && !snippet.ends_with(';'); if !snippet.ends_with('}') && !snippet.ends_with(';');
if cx.sess().source_map().is_multiline(block.span); if cx.sess().source_map().is_multiline(block.span);
then { then {
@ -52,17 +52,14 @@ impl<'tcx> LateLintPass<'tcx> for SemicolonIfNothingReturned {
if let ExprKind::DropTemps(..) = &expr.kind { if let ExprKind::DropTemps(..) = &expr.kind {
return; return;
} }
let sugg = sugg::Sugg::hir_with_macro_callsite(cx, expr, "..");
let suggestion = format!("{sugg};");
span_lint_and_sugg( span_lint_and_sugg(
cx, cx,
SEMICOLON_IF_NOTHING_RETURNED, SEMICOLON_IF_NOTHING_RETURNED,
expr.span.source_callsite(), expr.span.source_callsite(),
"consider adding a `;` to the last statement for consistent formatting", "consider adding a `;` to the last statement for consistent formatting",
"add a `;` here", "add a `;` here",
suggestion, format!("{snippet};"),
Applicability::MaybeIncorrect, app,
); );
} }
} }

View file

@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::get_parent_node; use clippy_utils::get_parent_node;
use clippy_utils::source::snippet_with_macro_callsite; use clippy_utils::source::snippet_with_context;
use clippy_utils::visitors::{for_each_local_assignment, for_each_value_source}; use clippy_utils::visitors::{for_each_local_assignment, for_each_value_source};
use core::ops::ControlFlow; use core::ops::ControlFlow;
use rustc_errors::Applicability; use rustc_errors::Applicability;
@ -52,12 +52,13 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>) {
"this let-binding has unit value", "this let-binding has unit value",
|diag| { |diag| {
if let Some(expr) = &local.init { if let Some(expr) = &local.init {
let snip = snippet_with_macro_callsite(cx, expr.span, "()"); let mut app = Applicability::MachineApplicable;
let snip = snippet_with_context(cx, expr.span, local.span.ctxt(), "()", &mut app).0;
diag.span_suggestion( diag.span_suggestion(
local.span, local.span,
"omit the `let` binding", "omit the `let` binding",
format!("{snip};"), format!("{snip};"),
Applicability::MachineApplicable, // snippet app,
); );
} }
}, },

View file

@ -1,5 +1,5 @@
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
use clippy_utils::source::{snippet, snippet_with_macro_callsite}; use clippy_utils::source::{snippet, snippet_with_context};
use clippy_utils::sugg::Sugg; use clippy_utils::sugg::Sugg;
use clippy_utils::ty::{is_copy, is_type_diagnostic_item, same_type_and_consts}; use clippy_utils::ty::{is_copy, is_type_diagnostic_item, same_type_and_consts};
use clippy_utils::{get_parent_expr, is_trait_method, match_def_path, path_to_local, paths}; use clippy_utils::{get_parent_expr, is_trait_method, match_def_path, path_to_local, paths};
@ -68,15 +68,16 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
let a = cx.typeck_results().expr_ty(e); let a = cx.typeck_results().expr_ty(e);
let b = cx.typeck_results().expr_ty(recv); let b = cx.typeck_results().expr_ty(recv);
if same_type_and_consts(a, b) { if same_type_and_consts(a, b) {
let sugg = snippet_with_macro_callsite(cx, recv.span, "<expr>").to_string(); let mut app = Applicability::MachineApplicable;
let sugg = snippet_with_context(cx, recv.span, e.span.ctxt(), "<expr>", &mut app).0;
span_lint_and_sugg( span_lint_and_sugg(
cx, cx,
USELESS_CONVERSION, USELESS_CONVERSION,
e.span, e.span,
&format!("useless conversion to the same type: `{b}`"), &format!("useless conversion to the same type: `{b}`"),
"consider removing `.into()`", "consider removing `.into()`",
sugg, sugg.into_owned(),
Applicability::MachineApplicable, // snippet app,
); );
} }
} }
@ -165,7 +166,8 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
if same_type_and_consts(a, b); if same_type_and_consts(a, b);
then { then {
let sugg = Sugg::hir_with_macro_callsite(cx, arg, "<expr>").maybe_par(); let mut app = Applicability::MachineApplicable;
let sugg = Sugg::hir_with_context(cx, arg, e.span.ctxt(), "<expr>", &mut app).maybe_par();
let sugg_msg = let sugg_msg =
format!("consider removing `{}()`", snippet(cx, path.span, "From::from")); format!("consider removing `{}()`", snippet(cx, path.span, "From::from"));
span_lint_and_sugg( span_lint_and_sugg(
@ -175,7 +177,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
&format!("useless conversion to the same type: `{b}`"), &format!("useless conversion to the same type: `{b}`"),
&sugg_msg, &sugg_msg,
sugg.to_string(), sugg.to_string(),
Applicability::MachineApplicable, // snippet app,
); );
} }
} }

View file

@ -12,24 +12,21 @@ use rustc_span::{BytePos, Pos, Span, SpanData, SyntaxContext, DUMMY_SP};
use std::borrow::Cow; use std::borrow::Cow;
/// Like `snippet_block`, but add braces if the expr is not an `ExprKind::Block`. /// Like `snippet_block`, but add braces if the expr is not an `ExprKind::Block`.
/// Also takes an `Option<String>` which can be put inside the braces. pub fn expr_block<T: LintContext>(
pub fn expr_block<'a, T: LintContext>(
cx: &T, cx: &T,
expr: &Expr<'_>, expr: &Expr<'_>,
option: Option<String>, outer: SyntaxContext,
default: &'a str, default: &str,
indent_relative_to: Option<Span>, indent_relative_to: Option<Span>,
) -> Cow<'a, str> { app: &mut Applicability,
let code = snippet_block(cx, expr.span, default, indent_relative_to); ) -> String {
let string = option.unwrap_or_default(); let (code, from_macro) = snippet_block_with_context(cx, expr.span, outer, default, indent_relative_to, app);
if expr.span.from_expansion() { if from_macro {
Cow::Owned(format!("{{ {} }}", snippet_with_macro_callsite(cx, expr.span, default))) format!("{{ {code} }}")
} else if let ExprKind::Block(_, _) = expr.kind { } else if let ExprKind::Block(_, _) = expr.kind {
Cow::Owned(format!("{code}{string}")) format!("{code}")
} else if string.is_empty() {
Cow::Owned(format!("{{ {code} }}"))
} else { } else {
Cow::Owned(format!("{{\n{code};\n{string}\n}}")) format!("{{ {code} }}")
} }
} }
@ -229,12 +226,6 @@ fn snippet_with_applicability_sess<'a>(
) )
} }
/// Same as `snippet`, but should only be used when it's clear that the input span is
/// not a macro argument.
pub fn snippet_with_macro_callsite<'a, T: LintContext>(cx: &T, span: Span, default: &'a str) -> Cow<'a, str> {
snippet(cx, span.source_callsite(), default)
}
/// Converts a span to a code snippet. Returns `None` if not available. /// Converts a span to a code snippet. Returns `None` if not available.
pub fn snippet_opt(cx: &impl LintContext, span: Span) -> Option<String> { pub fn snippet_opt(cx: &impl LintContext, span: Span) -> Option<String> {
snippet_opt_sess(cx.sess(), span) snippet_opt_sess(cx.sess(), span)
@ -303,6 +294,19 @@ pub fn snippet_block_with_applicability<'a>(
reindent_multiline(snip, true, indent) reindent_multiline(snip, true, indent)
} }
pub fn snippet_block_with_context<'a>(
cx: &impl LintContext,
span: Span,
outer: SyntaxContext,
default: &'a str,
indent_relative_to: Option<Span>,
app: &mut Applicability,
) -> (Cow<'a, str>, bool) {
let (snip, from_macro) = snippet_with_context(cx, span, outer, default, app);
let indent = indent_relative_to.and_then(|s| indent_of(cx, s));
(reindent_multiline(snip, true, indent), from_macro)
}
/// Same as `snippet_with_applicability`, but first walks the span up to the given context. This /// Same as `snippet_with_applicability`, but first walks the span up to the given context. This
/// will result in the macro call, rather then the expansion, if the span is from a child context. /// will result in the macro call, rather then the expansion, if the span is from a child context.
/// If the span is not from a child context, it will be used directly instead. /// If the span is not from a child context, it will be used directly instead.

View file

@ -1,9 +1,7 @@
//! Contains utility functions to generate suggestions. //! Contains utility functions to generate suggestions.
#![deny(clippy::missing_docs_in_private_items)] #![deny(clippy::missing_docs_in_private_items)]
use crate::source::{ use crate::source::{snippet, snippet_opt, snippet_with_applicability, snippet_with_context};
snippet, snippet_opt, snippet_with_applicability, snippet_with_context, snippet_with_macro_callsite,
};
use crate::ty::expr_sig; use crate::ty::expr_sig;
use crate::{get_parent_expr_for_hir, higher}; use crate::{get_parent_expr_for_hir, higher};
use rustc_ast::util::parser::AssocOp; use rustc_ast::util::parser::AssocOp;
@ -89,12 +87,6 @@ impl<'a> Sugg<'a> {
}) })
} }
/// Same as `hir`, but will use the pre expansion span if the `expr` was in a macro.
pub fn hir_with_macro_callsite(cx: &LateContext<'_>, expr: &hir::Expr<'_>, default: &'a str) -> Self {
let get_snippet = |span| snippet_with_macro_callsite(cx, span, default);
Self::hir_from_snippet(expr, get_snippet)
}
/// Same as `hir`, but first walks the span up to the given context. This will result in the /// Same as `hir`, but first walks the span up to the given context. This will result in the
/// macro call, rather then the expansion, if the span is from a child context. If the span is /// macro call, rather then the expansion, if the span is from a child context. If the span is
/// not from a child context, it will be used directly instead. /// not from a child context, it will be used directly instead.

View file

@ -69,8 +69,8 @@ fn issue5504() {
} }
fn try_result_opt() -> Result<i32, i32> { fn try_result_opt() -> Result<i32, i32> {
while (r#try!(result_opt())).is_some() {} while r#try!(result_opt()).is_some() {}
if (r#try!(result_opt())).is_some() {} if r#try!(result_opt()).is_some() {}
Ok(42) Ok(42)
} }

View file

@ -88,13 +88,13 @@ error: redundant pattern matching, consider using `is_some()`
--> $DIR/redundant_pattern_matching_result.rs:84:19 --> $DIR/redundant_pattern_matching_result.rs:84:19
| |
LL | while let Some(_) = r#try!(result_opt()) {} LL | while let Some(_) = r#try!(result_opt()) {}
| ----------^^^^^^^----------------------- help: try this: `while (r#try!(result_opt())).is_some()` | ----------^^^^^^^----------------------- help: try this: `while r#try!(result_opt()).is_some()`
error: redundant pattern matching, consider using `is_some()` error: redundant pattern matching, consider using `is_some()`
--> $DIR/redundant_pattern_matching_result.rs:85:16 --> $DIR/redundant_pattern_matching_result.rs:85:16
| |
LL | if let Some(_) = r#try!(result_opt()) {} LL | if let Some(_) = r#try!(result_opt()) {}
| -------^^^^^^^----------------------- help: try this: `if (r#try!(result_opt())).is_some()` | -------^^^^^^^----------------------- help: try this: `if r#try!(result_opt()).is_some()`
error: redundant pattern matching, consider using `is_some()` error: redundant pattern matching, consider using `is_some()`
--> $DIR/redundant_pattern_matching_result.rs:91:12 --> $DIR/redundant_pattern_matching_result.rs:91:12