@@ -3,13 +3,15 @@ use std::iter;
33use rustc_ast:: util:: { classify, parser} ;
44use rustc_ast:: { self as ast, ExprKind , HasAttrs as _, StmtKind } ;
55use rustc_attr_data_structures:: { AttributeKind , find_attr} ;
6+ use rustc_data_structures:: fx:: FxHashMap ;
67use rustc_errors:: { MultiSpan , pluralize} ;
78use rustc_hir:: def:: { DefKind , Res } ;
89use rustc_hir:: def_id:: DefId ;
910use rustc_hir:: { self as hir, LangItem } ;
1011use rustc_infer:: traits:: util:: elaborate;
1112use rustc_middle:: ty:: { self , Ty , adjustment} ;
1213use rustc_session:: { declare_lint, declare_lint_pass, impl_lint_pass} ;
14+ use rustc_span:: edition:: Edition :: Edition2015 ;
1315use rustc_span:: { BytePos , Span , Symbol , kw, sym} ;
1416use tracing:: instrument;
1517
@@ -1032,6 +1034,14 @@ pub(crate) struct UnusedParens {
10321034 /// `1 as (i32) < 2` parses to ExprKind::Lt
10331035 /// `1 as i32 < 2` parses to i32::<2[missing angle bracket]
10341036 parens_in_cast_in_lt : Vec < ast:: NodeId > ,
1037+ /// Ty nodes in this map are in TypeNoBounds position. Any bounds they
1038+ /// contain may be ambiguous w/r/t trailing `+` operators.
1039+ in_no_bounds_pos : FxHashMap < ast:: NodeId , NoBoundsException > ,
1040+ }
1041+
1042+ enum NoBoundsException {
1043+ None ,
1044+ OneBound ,
10351045}
10361046
10371047impl_lint_pass ! ( UnusedParens => [ UNUSED_PARENS ] ) ;
@@ -1276,10 +1286,12 @@ impl EarlyLintPass for UnusedParens {
12761286 }
12771287 ast:: TyKind :: Paren ( r) => {
12781288 match & r. kind {
1279- ast:: TyKind :: TraitObject ( ..) => { }
1289+ ast:: TyKind :: ImplTrait ( _, bounds) | ast:: TyKind :: TraitObject ( bounds, _)
1290+ if self . in_no_bounds_pos . get ( & ty. id ) . is_some_and ( |exception| {
1291+ matches ! ( exception, NoBoundsException :: None ) || bounds. len ( ) > 1
1292+ } ) => { }
12801293 ast:: TyKind :: BareFn ( b)
12811294 if self . with_self_ty_parens && b. generic_params . len ( ) > 0 => { }
1282- ast:: TyKind :: ImplTrait ( _, bounds) if bounds. len ( ) > 1 => { }
12831295 _ => {
12841296 let spans = if !ty. span . from_expansion ( ) {
12851297 r. span
@@ -1293,6 +1305,74 @@ impl EarlyLintPass for UnusedParens {
12931305 }
12941306 self . with_self_ty_parens = false ;
12951307 }
1308+ ast:: TyKind :: Ref ( _, mut_ty) | ast:: TyKind :: Ptr ( mut_ty) => {
1309+ self . in_no_bounds_pos . insert ( mut_ty. ty . id , NoBoundsException :: OneBound ) ;
1310+ }
1311+ ast:: TyKind :: TraitObject ( bounds, _) | ast:: TyKind :: ImplTrait ( _, bounds) => {
1312+ for i in 0 ..bounds. len ( ) {
1313+ let last = i == bounds. len ( ) - 1 ;
1314+
1315+ if let ast:: GenericBound :: Trait ( poly_trait_ref) = & bounds[ i] {
1316+ let parenthesized = cx
1317+ . sess ( )
1318+ . source_map ( )
1319+ . span_to_snippet ( poly_trait_ref. span )
1320+ . map ( |snip| snip. starts_with ( '(' ) && snip. ends_with ( ')' ) )
1321+ . unwrap_or ( false ) ;
1322+
1323+ let fn_with_explicit_ret_ty = if let [ .., segment] =
1324+ & * poly_trait_ref. trait_ref . path . segments
1325+ && let Some ( args) = segment. args . as_ref ( )
1326+ && let ast:: GenericArgs :: Parenthesized ( paren_args) = & * * args
1327+ && let ast:: FnRetTy :: Ty ( ret_ty) = & paren_args. output
1328+ {
1329+ self . in_no_bounds_pos . insert (
1330+ ret_ty. id ,
1331+ if last {
1332+ NoBoundsException :: OneBound
1333+ } else {
1334+ NoBoundsException :: None
1335+ } ,
1336+ ) ;
1337+
1338+ true
1339+ } else {
1340+ false
1341+ } ;
1342+
1343+ let dyn2015_exception = cx. sess ( ) . psess . edition == Edition2015
1344+ && matches ! ( ty. kind, ast:: TyKind :: TraitObject ( ..) )
1345+ && i == 0
1346+ && poly_trait_ref
1347+ . trait_ref
1348+ . path
1349+ . segments
1350+ . first ( )
1351+ . map ( |s| s. ident . name == kw:: PathRoot )
1352+ . unwrap_or ( false ) ;
1353+
1354+ if parenthesized && ( last || !fn_with_explicit_ret_ty) && !dyn2015_exception
1355+ {
1356+ let s = poly_trait_ref. span ;
1357+ let spans = ( !s. from_expansion ( ) ) . then ( || {
1358+ (
1359+ s. with_hi ( s. lo ( ) + rustc_span:: BytePos ( 1 ) ) ,
1360+ s. with_lo ( s. hi ( ) - rustc_span:: BytePos ( 1 ) ) ,
1361+ )
1362+ } ) ;
1363+
1364+ self . emit_unused_delims (
1365+ cx,
1366+ poly_trait_ref. span ,
1367+ spans,
1368+ "type" ,
1369+ ( false , false ) ,
1370+ false ,
1371+ ) ;
1372+ }
1373+ }
1374+ }
1375+ }
12961376 _ => { }
12971377 }
12981378 }
@@ -1301,6 +1381,10 @@ impl EarlyLintPass for UnusedParens {
13011381 <Self as UnusedDelimLint >:: check_item ( self , cx, item)
13021382 }
13031383
1384+ fn check_item_post ( & mut self , _: & EarlyContext < ' _ > , _: & rustc_ast:: Item ) {
1385+ self . in_no_bounds_pos . clear ( ) ;
1386+ }
1387+
13041388 fn enter_where_predicate ( & mut self , _: & EarlyContext < ' _ > , pred : & ast:: WherePredicate ) {
13051389 use rustc_ast:: { WhereBoundPredicate , WherePredicateKind } ;
13061390 if let WherePredicateKind :: BoundPredicate ( WhereBoundPredicate {
0 commit comments