@@ -1102,15 +1102,109 @@ fn check_call_args(
11021102 . map ( |param| param. name . clone ( ) )
11031103 . collect ( ) ;
11041104
1105- for arg in args {
1106- if arg. name . is_none ( )
1107- && !allow_positional_args
1108- && constructor_field_shorthand_name (
1109- allow_constructor_field_shorthand,
1105+ // Resolve each argument's target parameter name exactly once and reuse it
1106+ // across every per-arg validation phase below. The resolution depends only on
1107+ // the argument, its position, and these fixed inputs, so it is identical in
1108+ // each phase — sharing it does not change which diagnostics fire or in what
1109+ // order.
1110+ let resolved_names: Vec < Option < & str > > = args
1111+ . iter ( )
1112+ . enumerate ( )
1113+ . map ( |( index, arg) | {
1114+ resolved_arg_param_name (
11101115 arg,
1116+ index,
1117+ allow_constructor_field_shorthand,
1118+ allow_positional_args,
11111119 & param_names,
1120+ & signature_params,
11121121 )
1113- . is_none ( )
1122+ } )
1123+ . collect ( ) ;
1124+
1125+ check_argument_naming (
1126+ analyzer,
1127+ args,
1128+ & call_name,
1129+ allow_positional_args,
1130+ allow_constructor_field_shorthand,
1131+ & param_names,
1132+ & signature_params,
1133+ & resolved_names,
1134+ ) ;
1135+ check_argument_completeness (
1136+ analyzer,
1137+ & call_name,
1138+ call_span,
1139+ & signature_params,
1140+ & resolved_names,
1141+ ) ;
1142+ check_argument_effects (
1143+ analyzer,
1144+ args,
1145+ & call_name,
1146+ allow_positional_args,
1147+ & param_effects,
1148+ & resolved_names,
1149+ ) ;
1150+
1151+ let type_param_substitutions =
1152+ call_type_param_substitutions ( analyzer, Some ( function) , callee, args, signature) ;
1153+ check_generic_call_bounds (
1154+ analyzer,
1155+ function,
1156+ callee,
1157+ & call_name,
1158+ & signature,
1159+ & type_param_substitutions,
1160+ call_span,
1161+ ) ;
1162+ check_message_channel_payload (
1163+ analyzer,
1164+ function,
1165+ & call_name,
1166+ & signature,
1167+ & type_param_substitutions,
1168+ call_span,
1169+ ) ;
1170+
1171+ check_argument_types (
1172+ analyzer,
1173+ args,
1174+ & call_name,
1175+ signature,
1176+ & type_param_substitutions,
1177+ & resolved_names,
1178+ ) ;
1179+ check_argument_escaping (
1180+ analyzer,
1181+ args,
1182+ & call_name,
1183+ signature,
1184+ noescape_bindings,
1185+ local_closure_bindings,
1186+ & resolved_names,
1187+ ) ;
1188+ }
1189+
1190+ /// Phase 1: enforce argument naming rules — unnamed arguments where they are not
1191+ /// allowed, duplicate arguments, and unknown argument names.
1192+ #[ allow( clippy:: too_many_arguments) ]
1193+ fn check_argument_naming (
1194+ analyzer : & mut Analyzer < ' _ > ,
1195+ args : & [ HirCallArg ] ,
1196+ call_name : & str ,
1197+ allow_positional_args : bool ,
1198+ allow_constructor_field_shorthand : bool ,
1199+ param_names : & HashSet < String > ,
1200+ signature_params : & [ ParamSig ] ,
1201+ resolved_names : & [ Option < & str > ] ,
1202+ ) {
1203+ for arg in args {
1204+ if arg. name . is_none ( )
1205+ && !allow_positional_args
1206+ && constructor_field_shorthand_name ( allow_constructor_field_shorthand, arg, param_names)
1207+ . is_none ( )
11141208 {
11151209 analyzer. diagnostics . push (
11161210 Diagnostic :: error (
@@ -1131,15 +1225,8 @@ fn check_call_args(
11311225
11321226 let mut seen_names = HashSet :: new ( ) ;
11331227 let mut seen_positional_params = HashSet :: new ( ) ;
1134- for ( index, arg) in args. iter ( ) . enumerate ( ) {
1135- let Some ( name) = resolved_arg_param_name (
1136- arg,
1137- index,
1138- allow_constructor_field_shorthand,
1139- allow_positional_args,
1140- & param_names,
1141- & signature_params,
1142- ) else {
1228+ for ( arg, resolved) in args. iter ( ) . zip ( resolved_names) {
1229+ let Some ( name) = * resolved else {
11431230 continue ;
11441231 } ;
11451232 if arg. name . is_none ( ) && !seen_positional_params. insert ( name) {
@@ -1174,28 +1261,25 @@ fn check_call_args(
11741261 ) )
11751262 . with_fix (
11761263 "rename_argument" ,
1177- format ! ( "Use one of: {}." , join_param_names( & signature_params) ) ,
1264+ format ! ( "Use one of: {}." , join_param_names( signature_params) ) ,
11781265 "manual" ,
11791266 ) ,
11801267 ) ;
11811268 }
11821269 }
1270+ }
11831271
1184- let provided_names: HashSet < & str > = args
1185- . iter ( )
1186- . enumerate ( )
1187- . filter_map ( |( index, arg) | {
1188- resolved_arg_param_name (
1189- arg,
1190- index,
1191- allow_constructor_field_shorthand,
1192- allow_positional_args,
1193- & param_names,
1194- & signature_params,
1195- )
1196- } )
1197- . collect ( ) ;
1198- for param in & signature_params {
1272+ /// Phase 2: enforce argument completeness — every required parameter must be
1273+ /// provided.
1274+ fn check_argument_completeness (
1275+ analyzer : & mut Analyzer < ' _ > ,
1276+ call_name : & str ,
1277+ call_span : & Span ,
1278+ signature_params : & [ ParamSig ] ,
1279+ resolved_names : & [ Option < & str > ] ,
1280+ ) {
1281+ let provided_names: HashSet < & str > = resolved_names. iter ( ) . filter_map ( |name| * name) . collect ( ) ;
1282+ for param in signature_params {
11991283 if !provided_names. contains ( param. name . as_str ( ) ) {
12001284 analyzer. diagnostics . push (
12011285 Diagnostic :: error (
@@ -1219,16 +1303,20 @@ fn check_call_args(
12191303 ) ;
12201304 }
12211305 }
1306+ }
12221307
1223- for ( index, arg) in args. iter ( ) . enumerate ( ) {
1224- let Some ( name) = resolved_arg_param_name (
1225- arg,
1226- index,
1227- allow_constructor_field_shorthand,
1228- allow_positional_args,
1229- & param_names,
1230- & signature_params,
1231- ) else {
1308+ /// Phase 3: enforce call-site data effects (`read`/`mut`/`take`) for parameters
1309+ /// that declare one.
1310+ fn check_argument_effects (
1311+ analyzer : & mut Analyzer < ' _ > ,
1312+ args : & [ HirCallArg ] ,
1313+ call_name : & str ,
1314+ allow_positional_args : bool ,
1315+ param_effects : & HashMap < String , & ' static str > ,
1316+ resolved_names : & [ Option < & str > ] ,
1317+ ) {
1318+ for ( arg, resolved) in args. iter ( ) . zip ( resolved_names) {
1319+ let Some ( name) = * resolved else {
12321320 continue ;
12331321 } ;
12341322 let Some ( expected) = param_effects. get ( name) else {
@@ -1258,46 +1346,30 @@ fn check_call_args(
12581346 ) ;
12591347 }
12601348 }
1349+ }
12611350
1262- let type_param_substitutions =
1263- call_type_param_substitutions ( analyzer, Some ( function) , callee, args, signature) ;
1264- check_generic_call_bounds (
1265- analyzer,
1266- function,
1267- callee,
1268- & call_name,
1269- & signature,
1270- & type_param_substitutions,
1271- call_span,
1272- ) ;
1273- check_message_channel_payload (
1274- analyzer,
1275- function,
1276- & call_name,
1277- & signature,
1278- & type_param_substitutions,
1279- call_span,
1280- ) ;
1281-
1282- for ( index, arg) in args. iter ( ) . enumerate ( ) {
1283- let Some ( name) = resolved_arg_param_name (
1284- arg,
1285- index,
1286- allow_constructor_field_shorthand,
1287- allow_positional_args,
1288- & param_names,
1289- & signature_params,
1290- ) else {
1351+ /// Phase 4: check each argument's type against the resolved signature parameter
1352+ /// (after type-parameter substitution).
1353+ fn check_argument_types (
1354+ analyzer : & mut Analyzer < ' _ > ,
1355+ args : & [ HirCallArg ] ,
1356+ call_name : & str ,
1357+ signature : & FunctionSig ,
1358+ type_param_substitutions : & HashMap < String , String > ,
1359+ resolved_names : & [ Option < & str > ] ,
1360+ ) {
1361+ for ( arg, resolved) in args. iter ( ) . zip ( resolved_names) {
1362+ let Some ( name) = * resolved else {
12911363 continue ;
12921364 } ;
12931365 let Some ( expected_param) = signature. params . iter ( ) . find ( |param| param. name == name) else {
12941366 continue ;
12951367 } ;
12961368 let expected_type =
1297- substitute_type_params ( & expected_param. type_name , & type_param_substitutions) ;
1369+ substitute_type_params ( & expected_param. type_name , type_param_substitutions) ;
12981370 if check_fn_closure_contract (
12991371 analyzer,
1300- & call_name,
1372+ call_name,
13011373 name,
13021374 & expected_type,
13031375 & signature. type_params ,
@@ -1308,13 +1380,8 @@ fn check_call_args(
13081380 if type_contains_unresolved_generic ( & expected_type, & signature. type_params ) {
13091381 continue ;
13101382 }
1311- if check_argument_variant_payload_type (
1312- analyzer,
1313- & call_name,
1314- name,
1315- & expected_type,
1316- & arg. value ,
1317- ) {
1383+ if check_argument_variant_payload_type ( analyzer, call_name, name, & expected_type, & arg. value )
1384+ {
13181385 continue ;
13191386 }
13201387 let Some ( actual_type) = hir_expr_type_name ( & arg. value ) else {
@@ -1352,17 +1419,22 @@ fn check_call_args(
13521419 ) ) ;
13531420 }
13541421 }
1422+ }
13551423
1356- for ( index, arg) in args. iter ( ) . enumerate ( ) {
1357- let expected_param = resolved_arg_param_name (
1358- arg,
1359- index,
1360- allow_constructor_field_shorthand,
1361- allow_positional_args,
1362- & param_names,
1363- & signature_params,
1364- )
1365- . and_then ( |name| signature. params . iter ( ) . find ( |param| param. name == name) ) ;
1424+ /// Phase 5: enforce noescape and local-closure escape rules for each argument,
1425+ /// skipping parameters typed as `noescape` function types.
1426+ fn check_argument_escaping (
1427+ analyzer : & mut Analyzer < ' _ > ,
1428+ args : & [ HirCallArg ] ,
1429+ call_name : & str ,
1430+ signature : & FunctionSig ,
1431+ noescape_bindings : & HashMap < String , CallbackBinding > ,
1432+ local_closure_bindings : & HashMap < String , Span > ,
1433+ resolved_names : & [ Option < & str > ] ,
1434+ ) {
1435+ for ( arg, resolved) in args. iter ( ) . zip ( resolved_names) {
1436+ let expected_param =
1437+ resolved. and_then ( |name| signature. params . iter ( ) . find ( |param| param. name == name) ) ;
13661438 if expected_param. is_some_and ( |param| is_noescape_fn_type ( & param. type_name ) ) {
13671439 continue ;
13681440 }
@@ -1371,14 +1443,14 @@ fn check_call_args(
13711443 & arg. value ,
13721444 & arg. span ,
13731445 noescape_bindings,
1374- NoescapeEscapeContext :: Pass { callee : & call_name } ,
1446+ NoescapeEscapeContext :: Pass { callee : call_name } ,
13751447 ) ;
13761448 check_local_closure_escape (
13771449 analyzer,
13781450 & arg. value ,
13791451 & arg. span ,
13801452 local_closure_bindings,
1381- LocalClosureEscapeContext :: Pass { callee : & call_name } ,
1453+ LocalClosureEscapeContext :: Pass { callee : call_name } ,
13821454 ) ;
13831455 }
13841456}
0 commit comments