fix the error-causing suggestion of 'borrowed_box'

fix the error-causing suggestion of 'borrowed_box',
which missed parentheses and was ambiguous.
This commit is contained in:
rail 2020-10-21 09:42:00 +13:00
parent afbac8906e
commit e568a328f9
3 changed files with 98 additions and 12 deletions

View file

@ -10,9 +10,9 @@ use rustc_errors::{Applicability, DiagnosticBuilder};
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::intravisit::{walk_body, walk_expr, walk_ty, FnKind, NestedVisitorMap, Visitor}; use rustc_hir::intravisit::{walk_body, walk_expr, walk_ty, FnKind, NestedVisitorMap, Visitor};
use rustc_hir::{ use rustc_hir::{
BinOpKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, FnSig, GenericArg, GenericParamKind, HirId, ImplItem, BinOpKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, FnSig, GenericArg, GenericBounds, GenericParamKind, HirId,
ImplItemKind, Item, ItemKind, Lifetime, Lit, Local, MatchSource, MutTy, Mutability, Node, QPath, Stmt, StmtKind, ImplItem, ImplItemKind, Item, ItemKind, Lifetime, Lit, Local, MatchSource, MutTy, Mutability, Node, QPath, Stmt,
TraitFn, TraitItem, TraitItemKind, TyKind, UnOp, StmtKind, SyntheticTyParamKind, TraitFn, TraitItem, TraitItemKind, TyKind, UnOp,
}; };
use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::hir::map::Map; use rustc_middle::hir::map::Map;
@ -678,17 +678,30 @@ impl Types {
// details. // details.
return; return;
} }
// When trait objects or opaque types have lifetime or auto-trait bounds,
// we need to add parentheses to avoid a syntax error due to its ambiguity.
// Originally reported as the issue #3128.
let inner_snippet = snippet(cx, inner.span, "..");
let suggestion = match &inner.kind {
TyKind::TraitObject(bounds, lt_bound) if bounds.len() > 1 || !lt_bound.is_elided() => {
format!("&{}({})", ltopt, &inner_snippet)
},
TyKind::Path(qpath)
if get_bounds_if_impl_trait(cx, qpath, inner.hir_id)
.map_or(false, |bounds| bounds.len() > 1) =>
{
format!("&{}({})", ltopt, &inner_snippet)
},
_ => format!("&{}{}", ltopt, &inner_snippet),
};
span_lint_and_sugg( span_lint_and_sugg(
cx, cx,
BORROWED_BOX, BORROWED_BOX,
hir_ty.span, hir_ty.span,
"you seem to be trying to use `&Box<T>`. Consider using just `&T`", "you seem to be trying to use `&Box<T>`. Consider using just `&T`",
"try", "try",
format!( suggestion,
"&{}{}",
ltopt,
&snippet(cx, inner.span, "..")
),
// To make this `MachineApplicable`, at least one needs to check if it isn't a trait item // To make this `MachineApplicable`, at least one needs to check if it isn't a trait item
// because the trait impls of it will break otherwise; // because the trait impls of it will break otherwise;
// and there may be other cases that result in invalid code. // and there may be other cases that result in invalid code.
@ -721,6 +734,21 @@ fn is_any_trait(t: &hir::Ty<'_>) -> bool {
false false
} }
fn get_bounds_if_impl_trait<'tcx>(cx: &LateContext<'tcx>, qpath: &QPath<'_>, id: HirId) -> Option<GenericBounds<'tcx>> {
if_chain! {
if let Some(did) = qpath_res(cx, qpath, id).opt_def_id();
if let Some(node) = cx.tcx.hir().get_if_local(did);
if let Node::GenericParam(generic_param) = node;
if let GenericParamKind::Type { synthetic, .. } = generic_param.kind;
if synthetic == Some(SyntheticTyParamKind::ImplTrait);
then {
Some(generic_param.bounds)
} else {
None
}
}
}
declare_clippy_lint! { declare_clippy_lint! {
/// **What it does:** Checks for binding a unit value. /// **What it does:** Checks for binding a unit value.
/// ///

View file

@ -3,6 +3,8 @@
#![allow(unused_variables)] #![allow(unused_variables)]
#![allow(dead_code)] #![allow(dead_code)]
use std::fmt::Display;
pub fn test1(foo: &mut Box<bool>) { pub fn test1(foo: &mut Box<bool>) {
// Although this function could be changed to "&mut bool", // Although this function could be changed to "&mut bool",
// avoiding the Box, mutable references to boxes are not // avoiding the Box, mutable references to boxes are not
@ -89,6 +91,20 @@ pub fn test13(boxed_slice: &mut Box<[i32]>) {
*boxed_slice = data.into_boxed_slice(); *boxed_slice = data.into_boxed_slice();
} }
// The suggestion should include proper parentheses to avoid a syntax error.
pub fn test14(_display: &Box<dyn Display>) {}
pub fn test15(_display: &Box<dyn Display + Send>) {}
pub fn test16<'a>(_display: &'a Box<dyn Display + 'a>) {}
pub fn test17(_display: &Box<impl Display>) {}
pub fn test18(_display: &Box<impl Display + Send>) {}
pub fn test19<'a>(_display: &'a Box<impl Display + 'a>) {}
// This exists only to check what happens when parentheses are already present.
// Even though the current implementation doesn't put extra parentheses,
// it's fine that unnecessary parentheses appear in the future for some reason.
pub fn test20(_display: &Box<(dyn Display + Send)>) {}
fn main() { fn main() {
test1(&mut Box::new(false)); test1(&mut Box::new(false));
test2(); test2();

View file

@ -1,5 +1,5 @@
error: you seem to be trying to use `&Box<T>`. Consider using just `&T` error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
--> $DIR/borrow_box.rs:19:14 --> $DIR/borrow_box.rs:21:14
| |
LL | let foo: &Box<bool>; LL | let foo: &Box<bool>;
| ^^^^^^^^^^ help: try: `&bool` | ^^^^^^^^^^ help: try: `&bool`
@ -11,16 +11,58 @@ LL | #![deny(clippy::borrowed_box)]
| ^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^
error: you seem to be trying to use `&Box<T>`. Consider using just `&T` error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
--> $DIR/borrow_box.rs:23:10 --> $DIR/borrow_box.rs:25:10
| |
LL | foo: &'a Box<bool>, LL | foo: &'a Box<bool>,
| ^^^^^^^^^^^^^ help: try: `&'a bool` | ^^^^^^^^^^^^^ help: try: `&'a bool`
error: you seem to be trying to use `&Box<T>`. Consider using just `&T` error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
--> $DIR/borrow_box.rs:27:17 --> $DIR/borrow_box.rs:29:17
| |
LL | fn test4(a: &Box<bool>); LL | fn test4(a: &Box<bool>);
| ^^^^^^^^^^ help: try: `&bool` | ^^^^^^^^^^ help: try: `&bool`
error: aborting due to 3 previous errors error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
--> $DIR/borrow_box.rs:95:25
|
LL | pub fn test14(_display: &Box<dyn Display>) {}
| ^^^^^^^^^^^^^^^^^ help: try: `&dyn Display`
error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
--> $DIR/borrow_box.rs:96:25
|
LL | pub fn test15(_display: &Box<dyn Display + Send>) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&(dyn Display + Send)`
error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
--> $DIR/borrow_box.rs:97:29
|
LL | pub fn test16<'a>(_display: &'a Box<dyn Display + 'a>) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&'a (dyn Display + 'a)`
error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
--> $DIR/borrow_box.rs:99:25
|
LL | pub fn test17(_display: &Box<impl Display>) {}
| ^^^^^^^^^^^^^^^^^^ help: try: `&impl Display`
error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
--> $DIR/borrow_box.rs:100:25
|
LL | pub fn test18(_display: &Box<impl Display + Send>) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&(impl Display + Send)`
error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
--> $DIR/borrow_box.rs:101:29
|
LL | pub fn test19<'a>(_display: &'a Box<impl Display + 'a>) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&'a (impl Display + 'a)`
error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
--> $DIR/borrow_box.rs:106:25
|
LL | pub fn test20(_display: &Box<(dyn Display + Send)>) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&(dyn Display + Send)`
error: aborting due to 10 previous errors