Skip to content

Commit e568a32

Browse files
committed
fix the error-causing suggestion of 'borrowed_box'
fix the error-causing suggestion of 'borrowed_box', which missed parentheses and was ambiguous.
1 parent afbac89 commit e568a32

File tree

3 files changed

+98
-12
lines changed

3 files changed

+98
-12
lines changed

clippy_lints/src/types.rs

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ use rustc_errors::{Applicability, DiagnosticBuilder};
1010
use rustc_hir as hir;
1111
use rustc_hir::intravisit::{walk_body, walk_expr, walk_ty, FnKind, NestedVisitorMap, Visitor};
1212
use rustc_hir::{
13-
BinOpKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, FnSig, GenericArg, GenericParamKind, HirId, ImplItem,
14-
ImplItemKind, Item, ItemKind, Lifetime, Lit, Local, MatchSource, MutTy, Mutability, Node, QPath, Stmt, StmtKind,
15-
TraitFn, TraitItem, TraitItemKind, TyKind, UnOp,
13+
BinOpKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, FnSig, GenericArg, GenericBounds, GenericParamKind, HirId,
14+
ImplItem, ImplItemKind, Item, ItemKind, Lifetime, Lit, Local, MatchSource, MutTy, Mutability, Node, QPath, Stmt,
15+
StmtKind, SyntheticTyParamKind, TraitFn, TraitItem, TraitItemKind, TyKind, UnOp,
1616
};
1717
use rustc_lint::{LateContext, LateLintPass, LintContext};
1818
use rustc_middle::hir::map::Map;
@@ -678,17 +678,30 @@ impl Types {
678678
// details.
679679
return;
680680
}
681+
682+
// When trait objects or opaque types have lifetime or auto-trait bounds,
683+
// we need to add parentheses to avoid a syntax error due to its ambiguity.
684+
// Originally reported as the issue #3128.
685+
let inner_snippet = snippet(cx, inner.span, "..");
686+
let suggestion = match &inner.kind {
687+
TyKind::TraitObject(bounds, lt_bound) if bounds.len() > 1 || !lt_bound.is_elided() => {
688+
format!("&{}({})", ltopt, &inner_snippet)
689+
},
690+
TyKind::Path(qpath)
691+
if get_bounds_if_impl_trait(cx, qpath, inner.hir_id)
692+
.map_or(false, |bounds| bounds.len() > 1) =>
693+
{
694+
format!("&{}({})", ltopt, &inner_snippet)
695+
},
696+
_ => format!("&{}{}", ltopt, &inner_snippet),
697+
};
681698
span_lint_and_sugg(
682699
cx,
683700
BORROWED_BOX,
684701
hir_ty.span,
685702
"you seem to be trying to use `&Box<T>`. Consider using just `&T`",
686703
"try",
687-
format!(
688-
"&{}{}",
689-
ltopt,
690-
&snippet(cx, inner.span, "..")
691-
),
704+
suggestion,
692705
// To make this `MachineApplicable`, at least one needs to check if it isn't a trait item
693706
// because the trait impls of it will break otherwise;
694707
// and there may be other cases that result in invalid code.
@@ -721,6 +734,21 @@ fn is_any_trait(t: &hir::Ty<'_>) -> bool {
721734
false
722735
}
723736

737+
fn get_bounds_if_impl_trait<'tcx>(cx: &LateContext<'tcx>, qpath: &QPath<'_>, id: HirId) -> Option<GenericBounds<'tcx>> {
738+
if_chain! {
739+
if let Some(did) = qpath_res(cx, qpath, id).opt_def_id();
740+
if let Some(node) = cx.tcx.hir().get_if_local(did);
741+
if let Node::GenericParam(generic_param) = node;
742+
if let GenericParamKind::Type { synthetic, .. } = generic_param.kind;
743+
if synthetic == Some(SyntheticTyParamKind::ImplTrait);
744+
then {
745+
Some(generic_param.bounds)
746+
} else {
747+
None
748+
}
749+
}
750+
}
751+
724752
declare_clippy_lint! {
725753
/// **What it does:** Checks for binding a unit value.
726754
///

tests/ui/borrow_box.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
#![allow(unused_variables)]
44
#![allow(dead_code)]
55

6+
use std::fmt::Display;
7+
68
pub fn test1(foo: &mut Box<bool>) {
79
// Although this function could be changed to "&mut bool",
810
// avoiding the Box, mutable references to boxes are not
@@ -89,6 +91,20 @@ pub fn test13(boxed_slice: &mut Box<[i32]>) {
8991
*boxed_slice = data.into_boxed_slice();
9092
}
9193

94+
// The suggestion should include proper parentheses to avoid a syntax error.
95+
pub fn test14(_display: &Box<dyn Display>) {}
96+
pub fn test15(_display: &Box<dyn Display + Send>) {}
97+
pub fn test16<'a>(_display: &'a Box<dyn Display + 'a>) {}
98+
99+
pub fn test17(_display: &Box<impl Display>) {}
100+
pub fn test18(_display: &Box<impl Display + Send>) {}
101+
pub fn test19<'a>(_display: &'a Box<impl Display + 'a>) {}
102+
103+
// This exists only to check what happens when parentheses are already present.
104+
// Even though the current implementation doesn't put extra parentheses,
105+
// it's fine that unnecessary parentheses appear in the future for some reason.
106+
pub fn test20(_display: &Box<(dyn Display + Send)>) {}
107+
92108
fn main() {
93109
test1(&mut Box::new(false));
94110
test2();

tests/ui/borrow_box.stderr

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
2-
--> $DIR/borrow_box.rs:19:14
2+
--> $DIR/borrow_box.rs:21:14
33
|
44
LL | let foo: &Box<bool>;
55
| ^^^^^^^^^^ help: try: `&bool`
@@ -11,16 +11,58 @@ LL | #![deny(clippy::borrowed_box)]
1111
| ^^^^^^^^^^^^^^^^^^^^
1212

1313
error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
14-
--> $DIR/borrow_box.rs:23:10
14+
--> $DIR/borrow_box.rs:25:10
1515
|
1616
LL | foo: &'a Box<bool>,
1717
| ^^^^^^^^^^^^^ help: try: `&'a bool`
1818

1919
error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
20-
--> $DIR/borrow_box.rs:27:17
20+
--> $DIR/borrow_box.rs:29:17
2121
|
2222
LL | fn test4(a: &Box<bool>);
2323
| ^^^^^^^^^^ help: try: `&bool`
2424

25-
error: aborting due to 3 previous errors
25+
error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
26+
--> $DIR/borrow_box.rs:95:25
27+
|
28+
LL | pub fn test14(_display: &Box<dyn Display>) {}
29+
| ^^^^^^^^^^^^^^^^^ help: try: `&dyn Display`
30+
31+
error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
32+
--> $DIR/borrow_box.rs:96:25
33+
|
34+
LL | pub fn test15(_display: &Box<dyn Display + Send>) {}
35+
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&(dyn Display + Send)`
36+
37+
error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
38+
--> $DIR/borrow_box.rs:97:29
39+
|
40+
LL | pub fn test16<'a>(_display: &'a Box<dyn Display + 'a>) {}
41+
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&'a (dyn Display + 'a)`
42+
43+
error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
44+
--> $DIR/borrow_box.rs:99:25
45+
|
46+
LL | pub fn test17(_display: &Box<impl Display>) {}
47+
| ^^^^^^^^^^^^^^^^^^ help: try: `&impl Display`
48+
49+
error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
50+
--> $DIR/borrow_box.rs:100:25
51+
|
52+
LL | pub fn test18(_display: &Box<impl Display + Send>) {}
53+
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&(impl Display + Send)`
54+
55+
error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
56+
--> $DIR/borrow_box.rs:101:29
57+
|
58+
LL | pub fn test19<'a>(_display: &'a Box<impl Display + 'a>) {}
59+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&'a (impl Display + 'a)`
60+
61+
error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
62+
--> $DIR/borrow_box.rs:106:25
63+
|
64+
LL | pub fn test20(_display: &Box<(dyn Display + Send)>) {}
65+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&(dyn Display + Send)`
66+
67+
error: aborting due to 10 previous errors
2668

0 commit comments

Comments
 (0)