1 //! Contains utility functions to generate suggestions.
2 #![deny(clippy::missing_docs_in_private_items)]
5 use crate::source
::{snippet, snippet_opt, snippet_with_macro_callsite}
;
6 use rustc_ast
::util
::parser
::AssocOp
;
7 use rustc_ast
::{ast, token}
;
8 use rustc_ast_pretty
::pprust
::token_kind_to_string
;
9 use rustc_errors
::Applicability
;
11 use rustc_lint
::{EarlyContext, LateContext, LintContext}
;
12 use rustc_span
::source_map
::{CharPos, Span}
;
13 use rustc_span
::{BytePos, Pos}
;
15 use std
::convert
::TryInto
;
16 use std
::fmt
::Display
;
17 use std
::ops
::{Add, Neg, Not, Sub}
;
19 /// A helper type to build suggestion correctly handling parenthesis.
20 #[derive(Clone, PartialEq)]
22 /// An expression that never needs parenthesis such as `1337` or `[0; 42]`.
23 NonParen(Cow
<'a
, str>),
24 /// An expression that does not fit in other variants.
25 MaybeParen(Cow
<'a
, str>),
26 /// A binary operator expression, including `as`-casts and explicit type
28 BinOp(AssocOp
, Cow
<'a
, str>),
31 /// Literal constant `0`, for convenience.
32 pub const ZERO
: Sugg
<'
static> = Sugg
::NonParen(Cow
::Borrowed("0"));
33 /// Literal constant `1`, for convenience.
34 pub const ONE
: Sugg
<'
static> = Sugg
::NonParen(Cow
::Borrowed("1"));
35 /// a constant represents an empty string, for convenience.
36 pub const EMPTY
: Sugg
<'
static> = Sugg
::NonParen(Cow
::Borrowed(""));
38 impl Display
for Sugg
<'_
> {
39 fn fmt(&self, f
: &mut std
::fmt
::Formatter
<'_
>) -> Result
<(), std
::fmt
::Error
> {
41 Sugg
::NonParen(ref s
) | Sugg
::MaybeParen(ref s
) | Sugg
::BinOp(_
, ref s
) => s
.fmt(f
),
46 #[allow(clippy::wrong_self_convention)] // ok, because of the function `as_ty` method
48 /// Prepare a suggestion from an expression.
49 pub fn hir_opt(cx
: &LateContext
<'_
>, expr
: &hir
::Expr
<'_
>) -> Option
<Self> {
50 snippet_opt(cx
, expr
.span
).map(|snippet
| {
51 let snippet
= Cow
::Owned(snippet
);
52 Self::hir_from_snippet(expr
, snippet
)
56 /// Convenience function around `hir_opt` for suggestions with a default
58 pub fn hir(cx
: &LateContext
<'_
>, expr
: &hir
::Expr
<'_
>, default: &'a
str) -> Self {
59 Self::hir_opt(cx
, expr
).unwrap_or(Sugg
::NonParen(Cow
::Borrowed(default)))
62 /// Same as `hir`, but it adapts the applicability level by following rules:
64 /// - Applicability level `Unspecified` will never be changed.
65 /// - If the span is inside a macro, change the applicability level to `MaybeIncorrect`.
66 /// - If the default value is used and the applicability level is `MachineApplicable`, change it
69 pub fn hir_with_applicability(
73 applicability
: &mut Applicability
,
75 if *applicability
!= Applicability
::Unspecified
&& expr
.span
.from_expansion() {
76 *applicability
= Applicability
::MaybeIncorrect
;
78 Self::hir_opt(cx
, expr
).unwrap_or_else(|| {
79 if *applicability
== Applicability
::MachineApplicable
{
80 *applicability
= Applicability
::HasPlaceholders
;
82 Sugg
::NonParen(Cow
::Borrowed(default))
86 /// Same as `hir`, but will use the pre expansion span if the `expr` was in a macro.
87 pub fn hir_with_macro_callsite(cx
: &LateContext
<'_
>, expr
: &hir
::Expr
<'_
>, default: &'a
str) -> Self {
88 let snippet
= snippet_with_macro_callsite(cx
, expr
.span
, default);
90 Self::hir_from_snippet(expr
, snippet
)
93 /// Generate a suggestion for an expression with the given snippet. This is used by the `hir_*`
94 /// function variants of `Sugg`, since these use different snippet functions.
95 fn hir_from_snippet(expr
: &hir
::Expr
<'_
>, snippet
: Cow
<'a
, str>) -> Self {
96 if let Some(range
) = higher
::range(expr
) {
97 let op
= match range
.limits
{
98 ast
::RangeLimits
::HalfOpen
=> AssocOp
::DotDot
,
99 ast
::RangeLimits
::Closed
=> AssocOp
::DotDotEq
,
101 return Sugg
::BinOp(op
, snippet
);
105 hir
::ExprKind
::AddrOf(..)
106 | hir
::ExprKind
::Box(..)
107 | hir
::ExprKind
::If(..)
108 | hir
::ExprKind
::Closure(..)
109 | hir
::ExprKind
::Unary(..)
110 | hir
::ExprKind
::Match(..) => Sugg
::MaybeParen(snippet
),
111 hir
::ExprKind
::Continue(..)
112 | hir
::ExprKind
::Yield(..)
113 | hir
::ExprKind
::Array(..)
114 | hir
::ExprKind
::Block(..)
115 | hir
::ExprKind
::Break(..)
116 | hir
::ExprKind
::Call(..)
117 | hir
::ExprKind
::Field(..)
118 | hir
::ExprKind
::Index(..)
119 | hir
::ExprKind
::InlineAsm(..)
120 | hir
::ExprKind
::LlvmInlineAsm(..)
121 | hir
::ExprKind
::ConstBlock(..)
122 | hir
::ExprKind
::Lit(..)
123 | hir
::ExprKind
::Loop(..)
124 | hir
::ExprKind
::MethodCall(..)
125 | hir
::ExprKind
::Path(..)
126 | hir
::ExprKind
::Repeat(..)
127 | hir
::ExprKind
::Ret(..)
128 | hir
::ExprKind
::Struct(..)
129 | hir
::ExprKind
::Tup(..)
130 | hir
::ExprKind
::DropTemps(_
)
131 | hir
::ExprKind
::Err
=> Sugg
::NonParen(snippet
),
132 hir
::ExprKind
::Assign(..) => Sugg
::BinOp(AssocOp
::Assign
, snippet
),
133 hir
::ExprKind
::AssignOp(op
, ..) => Sugg
::BinOp(hirbinop2assignop(op
), snippet
),
134 hir
::ExprKind
::Binary(op
, ..) => Sugg
::BinOp(AssocOp
::from_ast_binop(higher
::binop(op
.node
)), snippet
),
135 hir
::ExprKind
::Cast(..) => Sugg
::BinOp(AssocOp
::As
, snippet
),
136 hir
::ExprKind
::Type(..) => Sugg
::BinOp(AssocOp
::Colon
, snippet
),
140 /// Prepare a suggestion from an expression.
141 pub fn ast(cx
: &EarlyContext
<'_
>, expr
: &ast
::Expr
, default: &'a
str) -> Self {
142 use rustc_ast
::ast
::RangeLimits
;
144 let snippet
= if expr
.span
.from_expansion() {
145 snippet_with_macro_callsite(cx
, expr
.span
, default)
147 snippet(cx
, expr
.span
, default)
151 ast
::ExprKind
::AddrOf(..)
152 | ast
::ExprKind
::Box(..)
153 | ast
::ExprKind
::Closure(..)
154 | ast
::ExprKind
::If(..)
155 | ast
::ExprKind
::Let(..)
156 | ast
::ExprKind
::Unary(..)
157 | ast
::ExprKind
::Match(..) => Sugg
::MaybeParen(snippet
),
158 ast
::ExprKind
::Async(..)
159 | ast
::ExprKind
::Block(..)
160 | ast
::ExprKind
::Break(..)
161 | ast
::ExprKind
::Call(..)
162 | ast
::ExprKind
::Continue(..)
163 | ast
::ExprKind
::Yield(..)
164 | ast
::ExprKind
::Field(..)
165 | ast
::ExprKind
::ForLoop(..)
166 | ast
::ExprKind
::Index(..)
167 | ast
::ExprKind
::InlineAsm(..)
168 | ast
::ExprKind
::LlvmInlineAsm(..)
169 | ast
::ExprKind
::ConstBlock(..)
170 | ast
::ExprKind
::Lit(..)
171 | ast
::ExprKind
::Loop(..)
172 | ast
::ExprKind
::MacCall(..)
173 | ast
::ExprKind
::MethodCall(..)
174 | ast
::ExprKind
::Paren(..)
175 | ast
::ExprKind
::Underscore
176 | ast
::ExprKind
::Path(..)
177 | ast
::ExprKind
::Repeat(..)
178 | ast
::ExprKind
::Ret(..)
179 | ast
::ExprKind
::Struct(..)
180 | ast
::ExprKind
::Try(..)
181 | ast
::ExprKind
::TryBlock(..)
182 | ast
::ExprKind
::Tup(..)
183 | ast
::ExprKind
::Array(..)
184 | ast
::ExprKind
::While(..)
185 | ast
::ExprKind
::Await(..)
186 | ast
::ExprKind
::Err
=> Sugg
::NonParen(snippet
),
187 ast
::ExprKind
::Range(.., RangeLimits
::HalfOpen
) => Sugg
::BinOp(AssocOp
::DotDot
, snippet
),
188 ast
::ExprKind
::Range(.., RangeLimits
::Closed
) => Sugg
::BinOp(AssocOp
::DotDotEq
, snippet
),
189 ast
::ExprKind
::Assign(..) => Sugg
::BinOp(AssocOp
::Assign
, snippet
),
190 ast
::ExprKind
::AssignOp(op
, ..) => Sugg
::BinOp(astbinop2assignop(op
), snippet
),
191 ast
::ExprKind
::Binary(op
, ..) => Sugg
::BinOp(AssocOp
::from_ast_binop(op
.node
), snippet
),
192 ast
::ExprKind
::Cast(..) => Sugg
::BinOp(AssocOp
::As
, snippet
),
193 ast
::ExprKind
::Type(..) => Sugg
::BinOp(AssocOp
::Colon
, snippet
),
197 /// Convenience method to create the `<lhs> && <rhs>` suggestion.
198 pub fn and(self, rhs
: &Self) -> Sugg
<'
static> {
199 make_binop(ast
::BinOpKind
::And
, &self, rhs
)
202 /// Convenience method to create the `<lhs> & <rhs>` suggestion.
203 pub fn bit_and(self, rhs
: &Self) -> Sugg
<'
static> {
204 make_binop(ast
::BinOpKind
::BitAnd
, &self, rhs
)
207 /// Convenience method to create the `<lhs> as <rhs>` suggestion.
208 pub fn as_ty
<R
: Display
>(self, rhs
: R
) -> Sugg
<'
static> {
209 make_assoc(AssocOp
::As
, &self, &Sugg
::NonParen(rhs
.to_string().into()))
212 /// Convenience method to create the `&<expr>` suggestion.
213 pub fn addr(self) -> Sugg
<'
static> {
217 /// Convenience method to create the `&mut <expr>` suggestion.
218 pub fn mut_addr(self) -> Sugg
<'
static> {
219 make_unop("&mut ", self)
222 /// Convenience method to create the `*<expr>` suggestion.
223 pub fn deref(self) -> Sugg
<'
static> {
227 /// Convenience method to create the `&*<expr>` suggestion. Currently this
228 /// is needed because `sugg.deref().addr()` produces an unnecessary set of
229 /// parentheses around the deref.
230 pub fn addr_deref(self) -> Sugg
<'
static> {
231 make_unop("&*", self)
234 /// Convenience method to create the `&mut *<expr>` suggestion. Currently
235 /// this is needed because `sugg.deref().mut_addr()` produces an unnecessary
236 /// set of parentheses around the deref.
237 pub fn mut_addr_deref(self) -> Sugg
<'
static> {
238 make_unop("&mut *", self)
241 /// Convenience method to transform suggestion into a return call
242 pub fn make_return(self) -> Sugg
<'
static> {
243 Sugg
::NonParen(Cow
::Owned(format
!("return {}", self)))
246 /// Convenience method to transform suggestion into a block
247 /// where the suggestion is a trailing expression
248 pub fn blockify(self) -> Sugg
<'
static> {
249 Sugg
::NonParen(Cow
::Owned(format
!("{{ {} }}", self)))
252 /// Convenience method to create the `<lhs>..<rhs>` or `<lhs>...<rhs>`
255 pub fn range(self, end
: &Self, limit
: ast
::RangeLimits
) -> Sugg
<'
static> {
257 ast
::RangeLimits
::HalfOpen
=> make_assoc(AssocOp
::DotDot
, &self, end
),
258 ast
::RangeLimits
::Closed
=> make_assoc(AssocOp
::DotDotEq
, &self, end
),
262 /// Adds parenthesis to any expression that might need them. Suitable to the
263 /// `self` argument of a method call
264 /// (e.g., to build `bar.foo()` or `(1 + 2).foo()`).
265 pub fn maybe_par(self) -> Self {
267 Sugg
::NonParen(..) => self,
268 // `(x)` and `(x).y()` both don't need additional parens.
269 Sugg
::MaybeParen(sugg
) => {
270 if has_enclosing_paren(&sugg
) {
271 Sugg
::MaybeParen(sugg
)
273 Sugg
::NonParen(format
!("({})", sugg
).into())
276 Sugg
::BinOp(_
, sugg
) => {
277 if has_enclosing_paren(&sugg
) {
280 Sugg
::NonParen(format
!("({})", sugg
).into())
287 /// Return `true` if `sugg` is enclosed in parenthesis.
288 fn has_enclosing_paren(sugg
: impl AsRef
<str>) -> bool
{
289 let mut chars
= sugg
.as_ref().chars();
290 if let Some('
('
) = chars
.next() {
292 while let Some(c
) = chars
.next() {
302 chars
.next().is_none()
308 // Copied from the rust standart library, and then edited
309 macro_rules
! forward_binop_impls_to_ref
{
310 (impl $imp
:ident
, $method
:ident
for $t
:ty
, type Output
= $o
:ty
) => {
311 impl $imp
<$t
> for &$t
{
314 fn $
method(self, other
: $t
) -> $o
{
315 $imp
::$
method(self, &other
)
319 impl $imp
<&$t
> for $t
{
322 fn $
method(self, other
: &$t
) -> $o
{
323 $imp
::$
method(&self, other
)
330 fn $
method(self, other
: $t
) -> $o
{
331 $imp
::$
method(&self, &other
)
337 impl Add
for &Sugg
<'_
> {
338 type Output
= Sugg
<'
static>;
339 fn add(self, rhs
: &Sugg
<'_
>) -> Sugg
<'
static> {
340 make_binop(ast
::BinOpKind
::Add
, self, rhs
)
344 impl Sub
for &Sugg
<'_
> {
345 type Output
= Sugg
<'
static>;
346 fn sub(self, rhs
: &Sugg
<'_
>) -> Sugg
<'
static> {
347 make_binop(ast
::BinOpKind
::Sub
, self, rhs
)
351 forward_binop_impls_to_ref
!(impl Add
, add
for Sugg
<'_
>, type Output
= Sugg
<'
static>);
352 forward_binop_impls_to_ref
!(impl Sub
, sub
for Sugg
<'_
>, type Output
= Sugg
<'
static>);
354 impl Neg
for Sugg
<'_
> {
355 type Output
= Sugg
<'
static>;
356 fn neg(self) -> Sugg
<'
static> {
361 impl Not
for Sugg
<'_
> {
362 type Output
= Sugg
<'
static>;
363 fn not(self) -> Sugg
<'
static> {
368 /// Helper type to display either `foo` or `(foo)`.
369 struct ParenHelper
<T
> {
370 /// `true` if parentheses are needed.
372 /// The main thing to display.
376 impl<T
> ParenHelper
<T
> {
377 /// Builds a `ParenHelper`.
378 fn new(paren
: bool
, wrapped
: T
) -> Self {
379 Self { paren, wrapped }
383 impl<T
: Display
> Display
for ParenHelper
<T
> {
384 fn fmt(&self, f
: &mut std
::fmt
::Formatter
<'_
>) -> Result
<(), std
::fmt
::Error
> {
386 write
!(f
, "({})", self.wrapped
)
393 /// Builds the string for `<op><expr>` adding parenthesis when necessary.
395 /// For convenience, the operator is taken as a string because all unary
396 /// operators have the same
398 pub fn make_unop(op
: &str, expr
: Sugg
<'_
>) -> Sugg
<'
static> {
399 Sugg
::MaybeParen(format
!("{}{}", op
, expr
.maybe_par()).into())
402 /// Builds the string for `<lhs> <op> <rhs>` adding parenthesis when necessary.
404 /// Precedence of shift operator relative to other arithmetic operation is
405 /// often confusing so
406 /// parenthesis will always be added for a mix of these.
407 pub fn make_assoc(op
: AssocOp
, lhs
: &Sugg
<'_
>, rhs
: &Sugg
<'_
>) -> Sugg
<'
static> {
408 /// Returns `true` if the operator is a shift operator `<<` or `>>`.
409 fn is_shift(op
: AssocOp
) -> bool
{
410 matches
!(op
, AssocOp
::ShiftLeft
| AssocOp
::ShiftRight
)
413 /// Returns `true` if the operator is a arithmetic operator
414 /// (i.e., `+`, `-`, `*`, `/`, `%`).
415 fn is_arith(op
: AssocOp
) -> bool
{
418 AssocOp
::Add
| AssocOp
::Subtract
| AssocOp
::Multiply
| AssocOp
::Divide
| AssocOp
::Modulus
422 /// Returns `true` if the operator `op` needs parenthesis with the operator
423 /// `other` in the direction `dir`.
424 fn needs_paren(op
: AssocOp
, other
: AssocOp
, dir
: Associativity
) -> bool
{
425 other
.precedence() < op
.precedence()
426 || (other
.precedence() == op
.precedence()
427 && ((op
!= other
&& associativity(op
) != dir
)
428 || (op
== other
&& associativity(op
) != Associativity
::Both
)))
429 || is_shift(op
) && is_arith(other
)
430 || is_shift(other
) && is_arith(op
)
433 let lhs_paren
= if let Sugg
::BinOp(lop
, _
) = *lhs
{
434 needs_paren(op
, lop
, Associativity
::Left
)
439 let rhs_paren
= if let Sugg
::BinOp(rop
, _
) = *rhs
{
440 needs_paren(op
, rop
, Associativity
::Right
)
445 let lhs
= ParenHelper
::new(lhs_paren
, lhs
);
446 let rhs
= ParenHelper
::new(rhs_paren
, rhs
);
447 let sugg
= match op
{
455 | AssocOp
::GreaterEqual
464 | AssocOp
::ShiftRight
465 | AssocOp
::Subtract
=> format
!(
468 op
.to_ast_binop().expect("Those are AST ops").to_string(),
471 AssocOp
::Assign
=> format
!("{} = {}", lhs
, rhs
),
472 AssocOp
::AssignOp(op
) => format
!("{} {}= {}", lhs
, token_kind_to_string(&token
::BinOp(op
)), rhs
),
473 AssocOp
::As
=> format
!("{} as {}", lhs
, rhs
),
474 AssocOp
::DotDot
=> format
!("{}..{}", lhs
, rhs
),
475 AssocOp
::DotDotEq
=> format
!("{}..={}", lhs
, rhs
),
476 AssocOp
::Colon
=> format
!("{}: {}", lhs
, rhs
),
479 Sugg
::BinOp(op
, sugg
.into())
482 /// Convenience wrapper around `make_assoc` and `AssocOp::from_ast_binop`.
483 pub fn make_binop(op
: ast
::BinOpKind
, lhs
: &Sugg
<'_
>, rhs
: &Sugg
<'_
>) -> Sugg
<'
static> {
484 make_assoc(AssocOp
::from_ast_binop(op
), lhs
, rhs
)
487 #[derive(PartialEq, Eq, Clone, Copy)]
488 /// Operator associativity.
490 /// The operator is both left-associative and right-associative.
492 /// The operator is left-associative.
494 /// The operator is not associative.
496 /// The operator is right-associative.
500 /// Returns the associativity/fixity of an operator. The difference with
501 /// `AssocOp::fixity` is that an operator can be both left and right associative
502 /// (such as `+`: `a + b + c == (a + b) + c == a + (b + c)`.
504 /// Chained `as` and explicit `:` type coercion never need inner parenthesis so
505 /// they are considered
508 fn associativity(op
: AssocOp
) -> Associativity
{
509 use rustc_ast
::util
::parser
::AssocOp
::{
510 Add
, As
, Assign
, AssignOp
, BitAnd
, BitOr
, BitXor
, Colon
, Divide
, DotDot
, DotDotEq
, Equal
, Greater
,
511 GreaterEqual
, LAnd
, LOr
, Less
, LessEqual
, Modulus
, Multiply
, NotEqual
, ShiftLeft
, ShiftRight
, Subtract
,
515 Assign
| AssignOp(_
) => Associativity
::Right
,
516 Add
| BitAnd
| BitOr
| BitXor
| LAnd
| LOr
| Multiply
| As
| Colon
=> Associativity
::Both
,
517 Divide
| Equal
| Greater
| GreaterEqual
| Less
| LessEqual
| Modulus
| NotEqual
| ShiftLeft
| ShiftRight
518 | Subtract
=> Associativity
::Left
,
519 DotDot
| DotDotEq
=> Associativity
::None
,
523 /// Converts a `hir::BinOp` to the corresponding assigning binary operator.
524 fn hirbinop2assignop(op
: hir
::BinOp
) -> AssocOp
{
525 use rustc_ast
::token
::BinOpToken
::{And, Caret, Minus, Or, Percent, Plus, Shl, Shr, Slash, Star}
;
527 AssocOp
::AssignOp(match op
.node
{
528 hir
::BinOpKind
::Add
=> Plus
,
529 hir
::BinOpKind
::BitAnd
=> And
,
530 hir
::BinOpKind
::BitOr
=> Or
,
531 hir
::BinOpKind
::BitXor
=> Caret
,
532 hir
::BinOpKind
::Div
=> Slash
,
533 hir
::BinOpKind
::Mul
=> Star
,
534 hir
::BinOpKind
::Rem
=> Percent
,
535 hir
::BinOpKind
::Shl
=> Shl
,
536 hir
::BinOpKind
::Shr
=> Shr
,
537 hir
::BinOpKind
::Sub
=> Minus
,
546 | hir
::BinOpKind
::Or
=> panic
!("This operator does not exist"),
550 /// Converts an `ast::BinOp` to the corresponding assigning binary operator.
551 fn astbinop2assignop(op
: ast
::BinOp
) -> AssocOp
{
552 use rustc_ast
::ast
::BinOpKind
::{
553 Add
, And
, BitAnd
, BitOr
, BitXor
, Div
, Eq
, Ge
, Gt
, Le
, Lt
, Mul
, Ne
, Or
, Rem
, Shl
, Shr
, Sub
,
555 use rustc_ast
::token
::BinOpToken
;
557 AssocOp
::AssignOp(match op
.node
{
558 Add
=> BinOpToken
::Plus
,
559 BitAnd
=> BinOpToken
::And
,
560 BitOr
=> BinOpToken
::Or
,
561 BitXor
=> BinOpToken
::Caret
,
562 Div
=> BinOpToken
::Slash
,
563 Mul
=> BinOpToken
::Star
,
564 Rem
=> BinOpToken
::Percent
,
565 Shl
=> BinOpToken
::Shl
,
566 Shr
=> BinOpToken
::Shr
,
567 Sub
=> BinOpToken
::Minus
,
568 And
| Eq
| Ge
| Gt
| Le
| Lt
| Ne
| Or
=> panic
!("This operator does not exist"),
572 /// Returns the indentation before `span` if there are nothing but `[ \t]`
573 /// before it on its line.
574 fn indentation
<T
: LintContext
>(cx
: &T
, span
: Span
) -> Option
<String
> {
575 let lo
= cx
.sess().source_map().lookup_char_pos(span
.lo());
577 .get_line(lo
.line
- 1 /* line numbers in `Loc` are 1-based */)
579 if let Some((pos
, _
)) = line
.char_indices().find(|&(_
, c
)| c
!= ' '
&& c
!= '
\t'
) {
580 // We can mix char and byte positions here because we only consider `[ \t]`.
581 if lo
.col
== CharPos(pos
) {
582 Some(line
[..pos
].into())
592 /// Convenience extension trait for `DiagnosticBuilder`.
593 pub trait DiagnosticBuilderExt
<T
: LintContext
> {
594 /// Suggests to add an attribute to an item.
596 /// Correctly handles indentation of the attribute and item.
601 /// diag.suggest_item_with_attr(cx, item, "#[derive(Default)]");
603 fn suggest_item_with_attr
<D
: Display
+ ?Sized
>(
609 applicability
: Applicability
,
612 /// Suggest to add an item before another.
614 /// The item should not be indented (except for inner indentation).
619 /// diag.suggest_prepend_item(cx, item,
624 fn suggest_prepend_item(&mut self, cx
: &T
, item
: Span
, msg
: &str, new_item
: &str, applicability
: Applicability
);
626 /// Suggest to completely remove an item.
628 /// This will remove an item and all following whitespace until the next non-whitespace
629 /// character. This should work correctly if item is on the same indentation level as the
635 /// diag.suggest_remove_item(cx, item, "remove this")
637 fn suggest_remove_item(&mut self, cx
: &T
, item
: Span
, msg
: &str, applicability
: Applicability
);
640 impl<T
: LintContext
> DiagnosticBuilderExt
<T
> for rustc_errors
::DiagnosticBuilder
<'_
> {
641 fn suggest_item_with_attr
<D
: Display
+ ?Sized
>(
647 applicability
: Applicability
,
649 if let Some(indent
) = indentation(cx
, item
) {
650 let span
= item
.with_hi(item
.lo());
652 self.span_suggestion(span
, msg
, format
!("{}\n{}", attr
, indent
), applicability
);
656 fn suggest_prepend_item(&mut self, cx
: &T
, item
: Span
, msg
: &str, new_item
: &str, applicability
: Applicability
) {
657 if let Some(indent
) = indentation(cx
, item
) {
658 let span
= item
.with_hi(item
.lo());
660 let mut first
= true;
661 let new_item
= new_item
668 format
!("{}{}\n", indent
, l
)
671 .collect
::<String
>();
673 self.span_suggestion(span
, msg
, format
!("{}\n{}", new_item
, indent
), applicability
);
677 fn suggest_remove_item(&mut self, cx
: &T
, item
: Span
, msg
: &str, applicability
: Applicability
) {
678 let mut remove_span
= item
;
679 let hi
= cx
.sess().source_map().next_point(remove_span
).hi();
680 let fmpos
= cx
.sess().source_map().lookup_byte_offset(hi
);
682 if let Some(ref src
) = fmpos
.sf
.src
{
683 let non_whitespace_offset
= src
[fmpos
.pos
.to_usize()..].find(|c
| c
!= ' '
&& c
!= '
\t'
&& c
!= '
\n'
);
685 if let Some(non_whitespace_offset
) = non_whitespace_offset
{
686 remove_span
= remove_span
687 .with_hi(remove_span
.hi() + BytePos(non_whitespace_offset
.try_into().expect("offset too large")))
691 self.span_suggestion(remove_span
, msg
, String
::new(), applicability
);
699 use rustc_ast
::util
::parser
::AssocOp
;
700 use std
::borrow
::Cow
;
702 const SUGGESTION
: Sugg
<'
static> = Sugg
::NonParen(Cow
::Borrowed("function_call()"));
705 fn make_return_transform_sugg_into_a_return_call() {
706 assert_eq
!("return function_call()", SUGGESTION
.make_return().to_string());
710 fn blockify_transforms_sugg_into_a_block() {
711 assert_eq
!("{ function_call() }", SUGGESTION
.blockify().to_string());
715 fn binop_maybe_par() {
716 let sugg
= Sugg
::BinOp(AssocOp
::Add
, "(1 + 1)".into());
717 assert_eq
!("(1 + 1)", sugg
.maybe_par().to_string());
719 let sugg
= Sugg
::BinOp(AssocOp
::Add
, "(1 + 1) + (1 + 1)".into());
720 assert_eq
!("((1 + 1) + (1 + 1))", sugg
.maybe_par().to_string());