@@ -10,7 +10,7 @@ use rustc_errors::{Applicability, DiagnosticBuilder};
10
10
use rustc_hir as hir;
11
11
use rustc_hir:: intravisit:: { walk_body, walk_expr, walk_ty, FnKind , NestedVisitorMap , Visitor } ;
12
12
use rustc_hir:: {
13
- BinOpKind , Body , Expr , ExprKind , FnDecl , FnRetTy , FnSig , GenericArg , GenericParamKind , HirId , ImplItem ,
13
+ BinOpKind , Block , Body , Expr , ExprKind , FnDecl , FnRetTy , FnSig , GenericArg , GenericParamKind , HirId , ImplItem ,
14
14
ImplItemKind , Item , ItemKind , Lifetime , Local , MatchSource , MutTy , Mutability , QPath , Stmt , StmtKind , TraitFn ,
15
15
TraitItem , TraitItemKind , TyKind , UnOp ,
16
16
} ;
@@ -29,10 +29,10 @@ use rustc_typeck::hir_ty_to_ty;
29
29
use crate :: consts:: { constant, Constant } ;
30
30
use crate :: utils:: paths;
31
31
use crate :: utils:: {
32
- clip, comparisons, differing_macro_contexts, higher, in_constant, int_bits, is_type_diagnostic_item,
32
+ clip, comparisons, differing_macro_contexts, higher, in_constant, indent_of , int_bits, is_type_diagnostic_item,
33
33
last_path_segment, match_def_path, match_path, method_chain_args, multispan_sugg, numeric_literal:: NumericLiteral ,
34
- qpath_res, same_tys, sext, snippet, snippet_opt , snippet_with_applicability , snippet_with_macro_callsite ,
35
- span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, unsext,
34
+ qpath_res, same_tys, sext, snippet, snippet_block_with_applicability , snippet_opt , snippet_with_applicability ,
35
+ snippet_with_macro_callsite , span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, unsext,
36
36
} ;
37
37
38
38
declare_clippy_lint ! {
@@ -779,31 +779,124 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnitArg {
779
779
780
780
match expr. kind {
781
781
ExprKind :: Call ( _, args) | ExprKind :: MethodCall ( _, _, args) => {
782
- for arg in args {
783
- if is_unit ( cx. tables . expr_ty ( arg) ) && !is_unit_literal ( arg) {
784
- if let ExprKind :: Match ( .., match_source) = & arg. kind {
785
- if * match_source == MatchSource :: TryDesugar {
786
- continue ;
782
+ let args_to_recover = args
783
+ . iter ( )
784
+ . filter ( |arg| {
785
+ if is_unit ( cx. tables . expr_ty ( arg) ) && !is_unit_literal ( arg) {
786
+ if let ExprKind :: Match ( .., MatchSource :: TryDesugar ) = & arg. kind {
787
+ false
788
+ } else {
789
+ true
787
790
}
791
+ } else {
792
+ false
788
793
}
789
-
790
- span_lint_and_sugg (
791
- cx,
792
- UNIT_ARG ,
793
- arg. span ,
794
- "passing a unit value to a function" ,
795
- "if you intended to pass a unit value, use a unit literal instead" ,
796
- "()" . to_string ( ) ,
797
- Applicability :: MaybeIncorrect ,
798
- ) ;
799
- }
794
+ } )
795
+ . collect :: < Vec < _ > > ( ) ;
796
+ if !args_to_recover. is_empty ( ) {
797
+ lint_unit_args ( cx, expr, & args_to_recover) ;
800
798
}
801
799
} ,
802
800
_ => ( ) ,
803
801
}
804
802
}
805
803
}
806
804
805
+ fn lint_unit_args ( cx : & LateContext < ' _ , ' _ > , expr : & Expr < ' _ > , args_to_recover : & [ & Expr < ' _ > ] ) {
806
+ let mut applicability = Applicability :: MachineApplicable ;
807
+ let ( singular, plural) = if args_to_recover. len ( ) > 1 {
808
+ ( "" , "s" )
809
+ } else {
810
+ ( "a " , "" )
811
+ } ;
812
+ span_lint_and_then (
813
+ cx,
814
+ UNIT_ARG ,
815
+ expr. span ,
816
+ & format ! ( "passing {}unit value{} to a function" , singular, plural) ,
817
+ |db| {
818
+ let mut or = "" ;
819
+ args_to_recover
820
+ . iter ( )
821
+ . filter_map ( |arg| {
822
+ if_chain ! {
823
+ if let ExprKind :: Block ( block, _) = arg. kind;
824
+ if block. expr. is_none( ) ;
825
+ if let Some ( last_stmt) = block. stmts. iter( ) . last( ) ;
826
+ if let StmtKind :: Semi ( last_expr) = last_stmt. kind;
827
+ if let Some ( snip) = snippet_opt( cx, last_expr. span) ;
828
+ then {
829
+ Some ( (
830
+ last_stmt. span,
831
+ snip,
832
+ ) )
833
+ }
834
+ else {
835
+ None
836
+ }
837
+ }
838
+ } )
839
+ . for_each ( |( span, sugg) | {
840
+ db. span_suggestion (
841
+ span,
842
+ "remove the semicolon from the last statement in the block" ,
843
+ sugg,
844
+ Applicability :: MaybeIncorrect ,
845
+ ) ;
846
+ or = "or " ;
847
+ } ) ;
848
+ let sugg = args_to_recover
849
+ . iter ( )
850
+ . filter ( |arg| !is_empty_block ( arg) )
851
+ . enumerate ( )
852
+ . map ( |( i, arg) | {
853
+ let indent = if i == 0 {
854
+ 0
855
+ } else {
856
+ indent_of ( cx, expr. span ) . unwrap_or ( 0 )
857
+ } ;
858
+ format ! (
859
+ "{}{};" ,
860
+ " " . repeat( indent) ,
861
+ snippet_block_with_applicability( cx, arg. span, ".." , Some ( expr. span) , & mut applicability)
862
+ )
863
+ } )
864
+ . collect :: < Vec < String > > ( ) ;
865
+ let mut and = "" ;
866
+ if !sugg. is_empty ( ) {
867
+ let plural = if sugg. len ( ) > 1 { "s" } else { "" } ;
868
+ db. span_suggestion (
869
+ expr. span . with_hi ( expr. span . lo ( ) ) ,
870
+ & format ! ( "{}move the expression{} in front of the call..." , or, plural) ,
871
+ format ! ( "{}\n " , sugg. join( "\n " ) ) ,
872
+ applicability,
873
+ ) ;
874
+ and = "...and "
875
+ }
876
+ db. multipart_suggestion (
877
+ & format ! ( "{}use {}unit literal{} instead" , and, singular, plural) ,
878
+ args_to_recover
879
+ . iter ( )
880
+ . map ( |arg| ( arg. span , "()" . to_string ( ) ) )
881
+ . collect :: < Vec < _ > > ( ) ,
882
+ applicability,
883
+ ) ;
884
+ } ,
885
+ ) ;
886
+ }
887
+
888
+ fn is_empty_block ( expr : & Expr < ' _ > ) -> bool {
889
+ matches ! (
890
+ expr. kind,
891
+ ExprKind :: Block (
892
+ Block {
893
+ stmts: & [ ] , expr: None , ..
894
+ } ,
895
+ _,
896
+ )
897
+ )
898
+ }
899
+
807
900
fn is_questionmark_desugar_marked_call ( expr : & Expr < ' _ > ) -> bool {
808
901
use rustc_span:: hygiene:: DesugaringKind ;
809
902
if let ExprKind :: Call ( ref callee, _) = expr. kind {
0 commit comments