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::source::snippet_with_macro_callsite;
use clippy_utils::source::snippet_with_context;
use clippy_utils::ty::{has_drop, is_copy};
use clippy_utils::{
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
// Default::default() get reassigned, unless the reassignment refers to the original binding
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`
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
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;
break;
}
@ -204,11 +206,12 @@ impl<'tcx> LateLintPass<'tcx> for Default {
.iter()
.all(|field| assigned_fields.iter().any(|(a, _)| a == &field.name));
let mut app = Applicability::Unspecified;
let field_list = assigned_fields
.into_iter()
.map(|(field, rhs)| {
// 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}")
})
.collect::<Vec<String>>()

View file

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

View file

@ -1,15 +1,17 @@
use super::SAME_ITEM_PUSH;
use clippy_utils::diagnostics::span_lint_and_help;
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 if_chain::if_chain;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::Applicability;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::intravisit::{walk_expr, Visitor};
use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, HirId, Mutability, Node, Pat, PatKind, Stmt, StmtKind};
use rustc_lint::LateContext;
use rustc_span::symbol::sym;
use rustc_span::SyntaxContext;
use std::iter::Iterator;
/// Detects for loop pushing the same item into a Vec
@ -20,9 +22,10 @@ pub(super) fn check<'tcx>(
body: &'tcx Expr<'_>,
_: &'tcx Expr<'_>,
) {
fn emit_lint(cx: &LateContext<'_>, vec: &Expr<'_>, pushed_item: &Expr<'_>) {
let vec_str = snippet_with_macro_callsite(cx, vec.span, "");
let item_str = snippet_with_macro_callsite(cx, pushed_item.span, "");
fn emit_lint(cx: &LateContext<'_>, vec: &Expr<'_>, pushed_item: &Expr<'_>, ctxt: SyntaxContext) {
let mut app = Applicability::Unspecified;
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(
cx,
@ -43,7 +46,7 @@ pub(super) fn check<'tcx>(
walk_expr(&mut same_item_push_visitor, body);
if_chain! {
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 ty = vec_ty.walk().nth(1).unwrap().expect_ty();
if cx
@ -69,11 +72,11 @@ pub(super) fn check<'tcx>(
then {
match init.kind {
// 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
ExprKind::Path(ref path) => {
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
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,
multiple_pushes: bool,
// 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>,
used_locals: FxHashSet<HirId>,
}
@ -118,7 +121,7 @@ impl<'a, 'tcx> SameItemPushVisitor<'a, 'tcx> {
if_chain! {
if !self.non_deterministic_expr;
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);
then {
!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
// 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! {
// Extract method being called
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 path.ident.name.as_str() == "push";
then {
return Some((self_expr, pushed_item))
return Some((self_expr, pushed_item, semi_stmt.span.ctxt()))
}
}
None

View file

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

View file

@ -10,9 +10,9 @@ use rustc_middle::ty;
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`.
if *cx.typeck_results().expr_ty(ex).kind() == ty::Bool {
if *cx.typeck_results().expr_ty(scrutinee).kind() == ty::Bool {
span_lint_and_then(
cx,
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 {
let mut app = Applicability::HasPlaceholders;
let ctxt = expr.span.ctxt();
let sugg = match (is_unit_expr(true_expr), is_unit_expr(false_expr)) {
(false, false) => Some(format!(
"if {} {} else {}",
snippet(cx, ex.span, "b"),
expr_block(cx, true_expr, None, "..", Some(expr.span)),
expr_block(cx, false_expr, None, "..", Some(expr.span))
snippet(cx, scrutinee.span, "b"),
expr_block(cx, true_expr, ctxt, "..", Some(expr.span), &mut app),
expr_block(cx, false_expr, ctxt, "..", Some(expr.span), &mut app)
)),
(false, true) => Some(format!(
"if {} {}",
snippet(cx, ex.span, "b"),
expr_block(cx, true_expr, None, "..", Some(expr.span))
snippet(cx, scrutinee.span, "b"),
expr_block(cx, true_expr, ctxt, "..", Some(expr.span), &mut app)
)),
(true, false) => {
let test = Sugg::hir(cx, ex, "..");
let test = Sugg::hir(cx, scrutinee, "..");
Some(format!(
"if {} {}",
!test,
expr_block(cx, false_expr, None, "..", Some(expr.span))
expr_block(cx, false_expr, ctxt, "..", Some(expr.span), &mut app)
))
},
(true, true) => None,

View file

@ -1,13 +1,14 @@
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 core::iter::once;
use rustc_errors::Applicability;
use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Pat, PatKind};
use rustc_lint::LateContext;
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
'b: 'a,
I: Clone + Iterator<Item = &'a Pat<'b>>,
@ -17,13 +18,28 @@ where
}
let (first_sugg, msg, title);
let span = ex.span.source_callsite();
if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = ex.kind {
first_sugg = once((span, Sugg::hir_with_macro_callsite(cx, inner, "..").to_string()));
let ctxt = expr.span.ctxt();
let mut app = Applicability::Unspecified;
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";
title = "you don't need to add `&` to both the expression and the patterns";
} 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";
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::macros::HirNode;
use clippy_utils::source::{indent_of, snippet, snippet_block, snippet_with_applicability};
use clippy_utils::sugg::Sugg;
use clippy_utils::source::{indent_of, snippet, snippet_block_with_context, snippet_with_applicability};
use clippy_utils::{get_parent_expr, is_refutable, peel_blocks};
use rustc_errors::Applicability;
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 bind_names = arms[0].pat.span;
let match_body = peel_blocks(arms[0].body);
let mut snippet_body = if match_body.span.from_expansion() {
Sugg::hir_with_macro_callsite(cx, match_body, "..").to_string()
} else {
snippet_block(cx, match_body.span, "..", Some(expr.span)).to_string()
};
let mut app = Applicability::MaybeIncorrect;
let (snippet_body, from_macro) = snippet_block_with_context(
cx,
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 ?
if let ExprKind::Block(block, _) = match_body.kind {
if matches!(match_body.kind, ExprKind::Block(..)) {
// 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(';');
}
}
let mut applicability = Applicability::MaybeIncorrect;
match arms[0].pat.kind {
PatKind::Binding(..) | PatKind::Tuple(_, _) | PatKind::Struct(..) => {
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),
(bind_names, matched_vars),
&snippet_body,
&mut applicability,
&mut app,
Some(span),
true,
);
@ -60,7 +63,7 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e
"this assignment could be simplified",
"consider removing the `match` expression",
sugg,
applicability,
app,
);
return;
@ -69,10 +72,10 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e
span,
format!(
"let {} = {};\n{}let {} = {snippet_body};",
snippet_with_applicability(cx, bind_names, "..", &mut applicability),
snippet_with_applicability(cx, matched_vars, "..", &mut applicability),
snippet_with_applicability(cx, bind_names, "..", &mut app),
snippet_with_applicability(cx, matched_vars, "..", &mut app),
" ".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 => {
@ -81,7 +84,7 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e
(ex, expr),
(bind_names, matched_vars),
&snippet_body,
&mut applicability,
&mut app,
None,
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",
"consider using a `let` statement",
sugg,
applicability,
app,
);
},
PatKind::Wild => {
@ -106,7 +109,7 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e
(ex, expr),
(bind_names, matched_vars),
&snippet_body,
&mut applicability,
&mut app,
None,
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",
"consider using the scrutinee and body instead",
sugg,
applicability,
app,
);
} else {
span_lint_and_sugg(

View file

@ -1,6 +1,6 @@
use super::REDUNDANT_PATTERN_MATCHING;
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::ty::{is_type_diagnostic_item, needs_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 ... = ... { ... }
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^
let expr_span = expr.span;
let ctxt = expr.span.ctxt();
// 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 ... = ... { ... }
// ^^^^^^^^^^^^^^^^^^^
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
} else {
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()
.to_string();

View file

@ -67,8 +67,10 @@ fn report_single_pattern(
els: Option<&Expr<'_>>,
) {
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| {
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);
@ -103,7 +105,7 @@ fn report_single_pattern(
// PartialEq for different reference counts may not exist.
"&".repeat(ref_count_diff),
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)
} else {
@ -112,21 +114,13 @@ fn report_single_pattern(
"if let {} = {} {}{els_str}",
snippet(cx, arms[0].pat.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)
}
};
span_lint_and_sugg(
cx,
lint,
expr.span,
msg,
"try this",
sugg,
Applicability::HasPlaceholders,
);
span_lint_and_sugg(cx, lint, expr.span, msg, "try this", sugg, app);
}
fn check_opt_like<'a>(

View file

@ -1,6 +1,6 @@
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::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 if_chain::if_chain;
use rustc_errors::Applicability;
@ -76,11 +76,8 @@ pub(crate) trait BindInsteadOfMap {
if !contains_return(inner_expr);
if let Some(msg) = Self::lint_msg(cx);
then {
let some_inner_snip = if inner_expr.span.from_expansion() {
snippet_with_macro_callsite(cx, inner_expr.span, "_")
} else {
snippet(cx, inner_expr.span, "_")
};
let mut app = Applicability::MachineApplicable;
let some_inner_snip = snippet_with_context(cx, inner_expr.span, closure_expr.span.ctxt(), "_", &mut app).0;
let closure_args_snip = snippet(cx, closure_args_span, "..");
let option_snip = snippet(cx, recv.span, "..");
@ -92,7 +89,7 @@ pub(crate) trait BindInsteadOfMap {
&msg,
"try this",
note,
Applicability::MachineApplicable,
app,
);
true
} else {

View file

@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
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 rustc_errors::Applicability;
use rustc_hir as hir;
@ -33,7 +33,9 @@ pub(super) fn check(
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(
cx,
@ -42,7 +44,7 @@ pub(super) fn check(
"using `.clone()` on a ref-counted pointer",
"try this",
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::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::{contains_return, is_trait_item, last_path_segment};
use if_chain::if_chain;
@ -9,7 +9,6 @@ use rustc_hir as hir;
use rustc_lint::LateContext;
use rustc_span::source_map::Span;
use rustc_span::symbol::{kw, sym, Symbol};
use std::borrow::Cow;
use super::OR_FUN_CALL;
@ -111,37 +110,24 @@ pub(super) fn check<'tcx>(
if poss.contains(&name);
then {
let ctxt = span.ctxt();
let mut app = Applicability::HasPlaceholders;
let sugg = {
let (snippet_span, use_lambda) = match (fn_has_arguments, fun_span) {
(false, Some(fun_span)) => (fun_span, false),
_ => (arg.span, true),
};
let format_span = |span: Span| {
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 = snippet_with_context(cx, snippet_span, ctxt, "..", &mut app).0;
let snip = if use_lambda {
let l_arg = if fn_has_arguments { "_" } else { "" };
format!("|{l_arg}| {snip}")
} else {
snip
snip.into_owned()
};
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}")
} else {
snip
@ -155,7 +141,7 @@ pub(super) fn check<'tcx>(
&format!("use of `{name}` followed by a function call"),
"try this",
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::source::{snippet, snippet_opt};
use clippy_utils::source::{snippet, snippet_opt, snippet_with_context};
use if_chain::if_chain;
use rustc_errors::Applicability;
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 Some(init) = local.init;
then {
// use the macro callsite when the init span (but not the whole local span)
// comes from an expansion like `vec![1, 2, 3]` in `let ref _ = vec![1, 2, 3];`
let sugg_init = if init.span.from_expansion() && !local.span.from_expansion() {
Sugg::hir_with_macro_callsite(cx, init, "..")
} else {
Sugg::hir(cx, init, "..")
};
let ctxt = local.span.ctxt();
let mut app = Applicability::MachineApplicable;
let sugg_init = Sugg::hir_with_context(cx, init, ctxt, "..", &mut app);
let (mutopt, initref) = if mutabl == Mutability::Mut {
("mut ", sugg_init.mut_addr())
} else {
("", sugg_init.addr())
};
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 {
String::new()
};
@ -212,7 +209,7 @@ impl<'tcx> LateLintPass<'tcx> for LintPass {
"let {name}{tyopt} = {initref};",
name=snippet(cx, name.span, ".."),
),
Applicability::MachineApplicable,
app,
);
}
);

View file

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

View file

@ -12,6 +12,7 @@ use rustc_hir::{
};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::SyntaxContext;
declare_clippy_lint! {
/// ### What it does
@ -95,10 +96,10 @@ struct OptionOccurrence {
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!(
"{}{}",
Sugg::hir_with_macro_callsite(cx, cond_expr, "..").maybe_par(),
cond_sugg.maybe_par(),
if as_mut {
".as_mut()"
} 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>(
cx: &LateContext<'tcx>,
ctxt: SyntaxContext,
pat: &Pat<'tcx>,
expr: &Expr<'_>,
if_then: &'tcx Expr<'_>,
@ -160,11 +162,23 @@ fn try_get_option_occurrence<'tcx>(
}
}
let mut app = Applicability::Unspecified;
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(),
some_expr: format!("|{capture_mut}{capture_name}| {}", Sugg::hir_with_macro_callsite(cx, some_body, "..")),
none_expr: format!("{}{}", if method_sugg == "map_or" { "" } else { "|| " }, Sugg::hir_with_macro_callsite(cx, none_body, "..")),
some_expr: format!(
"|{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)
{
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
@ -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> {
if let ExprKind::Match(ex, arms, MatchSource::Normal) = expr.kind {
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

View file

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

View file

@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_then;
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 core::ops::ControlFlow;
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",
|diag| {
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(
local.span,
"omit the `let` binding",
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::source::{snippet, snippet_with_macro_callsite};
use clippy_utils::source::{snippet, snippet_with_context};
use clippy_utils::sugg::Sugg;
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};
@ -68,15 +68,16 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
let a = cx.typeck_results().expr_ty(e);
let b = cx.typeck_results().expr_ty(recv);
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(
cx,
USELESS_CONVERSION,
e.span,
&format!("useless conversion to the same type: `{b}`"),
"consider removing `.into()`",
sugg,
Applicability::MachineApplicable, // snippet
sugg.into_owned(),
app,
);
}
}
@ -165,7 +166,8 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
if same_type_and_consts(a, b);
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 =
format!("consider removing `{}()`", snippet(cx, path.span, "From::from"));
span_lint_and_sugg(
@ -175,7 +177,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
&format!("useless conversion to the same type: `{b}`"),
&sugg_msg,
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;
/// 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<'a, T: LintContext>(
pub fn expr_block<T: LintContext>(
cx: &T,
expr: &Expr<'_>,
option: Option<String>,
default: &'a str,
outer: SyntaxContext,
default: &str,
indent_relative_to: Option<Span>,
) -> Cow<'a, str> {
let code = snippet_block(cx, expr.span, default, indent_relative_to);
let string = option.unwrap_or_default();
if expr.span.from_expansion() {
Cow::Owned(format!("{{ {} }}", snippet_with_macro_callsite(cx, expr.span, default)))
app: &mut Applicability,
) -> String {
let (code, from_macro) = snippet_block_with_context(cx, expr.span, outer, default, indent_relative_to, app);
if from_macro {
format!("{{ {code} }}")
} else if let ExprKind::Block(_, _) = expr.kind {
Cow::Owned(format!("{code}{string}"))
} else if string.is_empty() {
Cow::Owned(format!("{{ {code} }}"))
format!("{code}")
} 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.
pub fn snippet_opt(cx: &impl LintContext, span: Span) -> Option<String> {
snippet_opt_sess(cx.sess(), span)
@ -303,6 +294,19 @@ pub fn snippet_block_with_applicability<'a>(
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
/// 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.

View file

@ -1,9 +1,7 @@
//! Contains utility functions to generate suggestions.
#![deny(clippy::missing_docs_in_private_items)]
use crate::source::{
snippet, snippet_opt, snippet_with_applicability, snippet_with_context, snippet_with_macro_callsite,
};
use crate::source::{snippet, snippet_opt, snippet_with_applicability, snippet_with_context};
use crate::ty::expr_sig;
use crate::{get_parent_expr_for_hir, higher};
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
/// 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.

View file

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

View file

@ -88,13 +88,13 @@ error: redundant pattern matching, consider using `is_some()`
--> $DIR/redundant_pattern_matching_result.rs:84:19
|
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()`
--> $DIR/redundant_pattern_matching_result.rs:85:16
|
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()`
--> $DIR/redundant_pattern_matching_result.rs:91:12