@@ -5455,6 +5455,195 @@ static bool hasCurriedSelf(ConstraintSystem &cs, ConcreteDeclRef callee,
5455
5455
return false ;
5456
5456
}
5457
5457
5458
+ // / Determine the arity of the given parameter of function type.
5459
+ static unsigned functionParameterArity (AnyFunctionType::Param param) {
5460
+ Type paramType = param.getPlainType ();
5461
+ if (param.isVariadic ())
5462
+ paramType = ParamDecl::getVarargBaseTy (paramType);
5463
+
5464
+ paramType = paramType->lookThroughAllOptionalTypes ();
5465
+
5466
+ if (param.isAutoClosure ()) {
5467
+ paramType = paramType->castTo <AnyFunctionType>()->getResult ();
5468
+ paramType = paramType->lookThroughAllOptionalTypes ();
5469
+ }
5470
+
5471
+ return paramType->castTo <AnyFunctionType>()->getNumParams ();
5472
+ }
5473
+
5474
+ // / SE-0286 changed the direction in which the unlabeled trailing closure
5475
+ // / argument is matched to a parameter, from backward (the pre-Swift 5.3
5476
+ // / semantics) to forward (after SE-0286). Identify cases where this may
5477
+ // / have resulted in a silent change in behavior.
5478
+ static void maybeWarnAboutTrailingClosureBindingChange (
5479
+ ConcreteDeclRef callee,
5480
+ Expr *fn,
5481
+ Expr *arg,
5482
+ ArrayRef<AnyFunctionType::Param> args,
5483
+ ArrayRef<AnyFunctionType::Param> params,
5484
+ const ParameterListInfo ¶mInfo,
5485
+ Optional<unsigned > unlabeledTrailingClosureIndex,
5486
+ ArrayRef<ParamBinding> parameterBindings) {
5487
+
5488
+ if (!unlabeledTrailingClosureIndex)
5489
+ return ;
5490
+
5491
+ if (*unlabeledTrailingClosureIndex != args.size () - 1 )
5492
+ return ;
5493
+
5494
+ // Find the parameter that bound the unlabeled trailing closure argument.
5495
+ unsigned paramIdx;
5496
+ for (paramIdx = 0 ; paramIdx != parameterBindings.size (); ++paramIdx) {
5497
+ if (llvm::find (parameterBindings[paramIdx], *unlabeledTrailingClosureIndex)
5498
+ != parameterBindings[paramIdx].end ())
5499
+ break ;
5500
+ }
5501
+ assert (paramIdx != parameterBindings.size () && " Unbound trailing closure!" );
5502
+
5503
+ // If this parameter requires an argument, it would have been unfilled
5504
+ // prior to SE-2086; there is nothing to diagnose.
5505
+ if (parameterRequiresArgument (params, paramInfo, paramIdx))
5506
+ return ;
5507
+
5508
+ // Look for a later parameter that could match a trailing closure; the
5509
+ // last one of these would have been picked prior to SE-0286.
5510
+ Optional<unsigned > matchingBackwardParamIdx;
5511
+ for (unsigned backwardParamIdx :
5512
+ range (paramIdx + 1 , parameterBindings.size ())) {
5513
+ if (!paramInfo.acceptsUnlabeledTrailingClosureArgument (backwardParamIdx))
5514
+ continue ;
5515
+
5516
+ matchingBackwardParamIdx = backwardParamIdx;
5517
+ }
5518
+
5519
+ // If there is no other parameter that could match the unlabeled trailing
5520
+ // closure, there is nothing to diagnose.
5521
+ if (!matchingBackwardParamIdx)
5522
+ return ;
5523
+
5524
+ // Do a simple arity check; if the matched parameter and backward-matched
5525
+ // parameter accept functions with different arity, this would not have
5526
+ // type-checked with the backward scan, so there is nothing to report.
5527
+ if (functionParameterArity (params[paramIdx]) !=
5528
+ functionParameterArity (params[*matchingBackwardParamIdx]))
5529
+ return ;
5530
+
5531
+ // Compute the various source locations where diagnostics will occur.
5532
+ ASTContext &ctx = params[paramIdx].getPlainType ()->getASTContext ();
5533
+ Expr *trailingClosure;
5534
+ SourceLoc existingRParenLoc;
5535
+ SourceLoc leadingCommaLoc;
5536
+ if (auto tupleExpr = dyn_cast<TupleExpr>(arg)) {
5537
+ trailingClosure = tupleExpr->getElements ().back ();
5538
+ existingRParenLoc = tupleExpr->getRParenLoc ();
5539
+ assert (tupleExpr->getNumElements () >= 2 && " Should be a ParenExpr?" );
5540
+ leadingCommaLoc = Lexer::getLocForEndOfToken (
5541
+ ctx.SourceMgr ,
5542
+ tupleExpr->getElements ()[tupleExpr->getNumElements ()-2 ]->getEndLoc ());
5543
+ } else {
5544
+ auto parenExpr = cast<ParenExpr>(arg);
5545
+ trailingClosure = parenExpr->getSubExpr ();
5546
+ existingRParenLoc = parenExpr->getRParenLoc ();
5547
+ }
5548
+
5549
+ // Determine the names of the parameters that would be matched by the
5550
+ // forward and backward scans.
5551
+ Identifier paramName = params[paramIdx].getLabel ();
5552
+ Identifier backwardParamName = params[*matchingBackwardParamIdx].getLabel ();
5553
+
5554
+ // Produce a diagnostic referencing the callee.
5555
+ auto noteCallee = [&] {
5556
+ auto decl = callee.getDecl ();
5557
+ if (!decl)
5558
+ return ;
5559
+
5560
+ auto diag = ctx.Diags .diagnose (
5561
+ decl, diag::decl_multiple_defaulted_closure_parameters,
5562
+ decl->getName (), paramName, backwardParamName);
5563
+
5564
+ // Dig out the parameter declarations so we can highlight them.
5565
+ // FIXME: There should be a utility for this.
5566
+ const ParameterList *paramList = nullptr ;
5567
+ if (auto *func = dyn_cast<AbstractFunctionDecl>(decl)) {
5568
+ paramList = func->getParameters ();
5569
+ } else if (auto *subscript = dyn_cast<SubscriptDecl>(decl)) {
5570
+ paramList = subscript->getIndices ();
5571
+ } else if (auto *enumElement = dyn_cast<EnumElementDecl>(decl)) {
5572
+ paramList = enumElement->getParameterList ();
5573
+ }
5574
+
5575
+ if (paramList) {
5576
+ diag.highlight (paramList->get (paramIdx)->getLoc ());
5577
+ diag.highlight (paramList->get (*matchingBackwardParamIdx)->getLoc ());
5578
+ }
5579
+ };
5580
+
5581
+ // If the parameters have the same name, provide a custom diagnostic and then
5582
+ // bail out early; there are no useful notes we can provide here.
5583
+ if (paramName == backwardParamName) {
5584
+ ctx.Diags .diagnose (trailingClosure->getStartLoc (), diag::unlabeled_trailing_closure_changed_behavior_same_param_name,
5585
+ paramName);
5586
+ noteCallee ();
5587
+ return ;
5588
+ }
5589
+
5590
+ // Produce the diagnostic.
5591
+ ctx.Diags .diagnose (trailingClosure->getStartLoc (), diag::unlabeled_trailing_closure_changed_behavior, paramName,
5592
+ backwardParamName);
5593
+
5594
+ // Produce a note with a Fix-It describing how to resolve the ambiguity.
5595
+ auto diagResolution = [&](Identifier paramName, unsigned which) {
5596
+ // Emit the note.
5597
+ auto diag = ctx.Diags .diagnose (
5598
+ trailingClosure->getStartLoc (), diag::trailing_closure_select_parameter,
5599
+ paramName, which);
5600
+
5601
+ // Figure out the text to be inserted before the trailing closure.
5602
+ SmallString<16 > insertionText;
5603
+ SourceLoc insertionLoc;
5604
+ if (leadingCommaLoc.isValid ()) {
5605
+ insertionText += " , " ;
5606
+ assert (existingRParenLoc.isValid ());
5607
+ insertionLoc = leadingCommaLoc;
5608
+ } else if (existingRParenLoc.isInvalid ()) {
5609
+ insertionText += " (" ;
5610
+ insertionLoc = Lexer::getLocForEndOfToken (
5611
+ ctx.SourceMgr , fn->getEndLoc ());
5612
+ } else {
5613
+ insertionLoc = existingRParenLoc;
5614
+ }
5615
+
5616
+ // Add the label, if there is one.
5617
+ if (!paramName.empty ()) {
5618
+ insertionText += paramName.str ();
5619
+ insertionText += " : " ;
5620
+ }
5621
+
5622
+ // If there is an existing right parentheses, remove it while we
5623
+ // insert the new text.
5624
+ if (existingRParenLoc.isValid ()) {
5625
+ SourceLoc afterExistingRParenLoc = Lexer::getLocForEndOfToken (
5626
+ ctx.SourceMgr , existingRParenLoc);
5627
+ diag.fixItReplaceChars (
5628
+ insertionLoc, afterExistingRParenLoc, insertionText);
5629
+ } else {
5630
+ // Insert the appropriate prefix.
5631
+ diag.fixItInsert (insertionLoc, insertionText);
5632
+ }
5633
+
5634
+
5635
+ // Insert a right parenthesis after the closing '}' of the trailing closure;
5636
+ SourceLoc newRParenLoc = Lexer::getLocForEndOfToken (
5637
+ ctx.SourceMgr , trailingClosure->getEndLoc ());
5638
+ diag.fixItInsert (newRParenLoc, " )" );
5639
+ };
5640
+
5641
+ diagResolution (backwardParamName, 0 );
5642
+ diagResolution (paramName, 1 );
5643
+
5644
+ noteCallee ();
5645
+ }
5646
+
5458
5647
Expr *ExprRewriter::coerceCallArguments (
5459
5648
Expr *arg, AnyFunctionType *funcType,
5460
5649
ConcreteDeclRef callee,
@@ -5531,6 +5720,12 @@ Expr *ExprRewriter::coerceCallArguments(
5531
5720
// FIXME: Eventually, we want to enforce that we have either argTuple or
5532
5721
// argParen here.
5533
5722
5723
+ // Warn if there was a recent change in trailing closure binding semantics
5724
+ // that might have lead to a silent change in behavior.
5725
+ maybeWarnAboutTrailingClosureBindingChange (
5726
+ callee, apply->getFn (), arg, args, params, paramInfo,
5727
+ unlabeledTrailingClosureIndex, parameterBindings);
5728
+
5534
5729
SourceLoc lParenLoc, rParenLoc;
5535
5730
if (argTuple) {
5536
5731
lParenLoc = argTuple->getLParenLoc ();
0 commit comments