@@ -5521,6 +5521,195 @@ static bool hasCurriedSelf(ConstraintSystem &cs, ConcreteDeclRef callee,
5521
5521
return false ;
5522
5522
}
5523
5523
5524
+ // / Determine the arity of the given parameter of function type.
5525
+ static unsigned functionParameterArity (AnyFunctionType::Param param) {
5526
+ Type paramType = param.getPlainType ();
5527
+ if (param.isVariadic ())
5528
+ paramType = ParamDecl::getVarargBaseTy (paramType);
5529
+
5530
+ paramType = paramType->lookThroughAllOptionalTypes ();
5531
+
5532
+ if (param.isAutoClosure ()) {
5533
+ paramType = paramType->castTo <AnyFunctionType>()->getResult ();
5534
+ paramType = paramType->lookThroughAllOptionalTypes ();
5535
+ }
5536
+
5537
+ return paramType->castTo <AnyFunctionType>()->getNumParams ();
5538
+ }
5539
+
5540
+ // / SE-0286 changed the direction in which the unlabeled trailing closure
5541
+ // / argument is matched to a parameter, from backward (the pre-Swift 5.3
5542
+ // / semantics) to forward (after SE-0286). Identify cases where this may
5543
+ // / have resulted in a silent change in behavior.
5544
+ static void maybeWarnAboutTrailingClosureBindingChange (
5545
+ ConcreteDeclRef callee,
5546
+ Expr *fn,
5547
+ Expr *arg,
5548
+ ArrayRef<AnyFunctionType::Param> args,
5549
+ ArrayRef<AnyFunctionType::Param> params,
5550
+ const ParameterListInfo ¶mInfo,
5551
+ Optional<unsigned > unlabeledTrailingClosureIndex,
5552
+ ArrayRef<ParamBinding> parameterBindings) {
5553
+
5554
+ if (!unlabeledTrailingClosureIndex)
5555
+ return ;
5556
+
5557
+ if (*unlabeledTrailingClosureIndex != args.size () - 1 )
5558
+ return ;
5559
+
5560
+ // Find the parameter that bound the unlabeled trailing closure argument.
5561
+ unsigned paramIdx;
5562
+ for (paramIdx = 0 ; paramIdx != parameterBindings.size (); ++paramIdx) {
5563
+ if (llvm::find (parameterBindings[paramIdx], *unlabeledTrailingClosureIndex)
5564
+ != parameterBindings[paramIdx].end ())
5565
+ break ;
5566
+ }
5567
+ assert (paramIdx != parameterBindings.size () && " Unbound trailing closure!" );
5568
+
5569
+ // If this parameter requires an argument, it would have been unfilled
5570
+ // prior to SE-2086; there is nothing to diagnose.
5571
+ if (parameterRequiresArgument (params, paramInfo, paramIdx))
5572
+ return ;
5573
+
5574
+ // Look for a later parameter that could match a trailing closure; the
5575
+ // last one of these would have been picked prior to SE-0286.
5576
+ Optional<unsigned > matchingBackwardParamIdx;
5577
+ for (unsigned backwardParamIdx :
5578
+ range (paramIdx + 1 , parameterBindings.size ())) {
5579
+ if (!paramInfo.acceptsUnlabeledTrailingClosureArgument (backwardParamIdx))
5580
+ continue ;
5581
+
5582
+ matchingBackwardParamIdx = backwardParamIdx;
5583
+ }
5584
+
5585
+ // If there is no other parameter that could match the unlabeled trailing
5586
+ // closure, there is nothing to diagnose.
5587
+ if (!matchingBackwardParamIdx)
5588
+ return ;
5589
+
5590
+ // Do a simple arity check; if the matched parameter and backward-matched
5591
+ // parameter accept functions with different arity, this would not have
5592
+ // type-checked with the backward scan, so there is nothing to report.
5593
+ if (functionParameterArity (params[paramIdx]) !=
5594
+ functionParameterArity (params[*matchingBackwardParamIdx]))
5595
+ return ;
5596
+
5597
+ // Compute the various source locations where diagnostics will occur.
5598
+ ASTContext &ctx = params[paramIdx].getPlainType ()->getASTContext ();
5599
+ Expr *trailingClosure;
5600
+ SourceLoc existingRParenLoc;
5601
+ SourceLoc leadingCommaLoc;
5602
+ if (auto tupleExpr = dyn_cast<TupleExpr>(arg)) {
5603
+ trailingClosure = tupleExpr->getElements ().back ();
5604
+ existingRParenLoc = tupleExpr->getRParenLoc ();
5605
+ assert (tupleExpr->getNumElements () >= 2 && " Should be a ParenExpr?" );
5606
+ leadingCommaLoc = Lexer::getLocForEndOfToken (
5607
+ ctx.SourceMgr ,
5608
+ tupleExpr->getElements ()[tupleExpr->getNumElements ()-2 ]->getEndLoc ());
5609
+ } else {
5610
+ auto parenExpr = cast<ParenExpr>(arg);
5611
+ trailingClosure = parenExpr->getSubExpr ();
5612
+ existingRParenLoc = parenExpr->getRParenLoc ();
5613
+ }
5614
+
5615
+ // Determine the names of the parameters that would be matched by the
5616
+ // forward and backward scans.
5617
+ Identifier paramName = params[paramIdx].getLabel ();
5618
+ Identifier backwardParamName = params[*matchingBackwardParamIdx].getLabel ();
5619
+
5620
+ // Produce a diagnostic referencing the callee.
5621
+ auto noteCallee = [&] {
5622
+ auto decl = callee.getDecl ();
5623
+ if (!decl)
5624
+ return ;
5625
+
5626
+ auto diag = ctx.Diags .diagnose (
5627
+ decl, diag::decl_multiple_defaulted_closure_parameters,
5628
+ decl->getName (), paramName, backwardParamName);
5629
+
5630
+ // Dig out the parameter declarations so we can highlight them.
5631
+ // FIXME: There should be a utility for this.
5632
+ const ParameterList *paramList = nullptr ;
5633
+ if (auto *func = dyn_cast<AbstractFunctionDecl>(decl)) {
5634
+ paramList = func->getParameters ();
5635
+ } else if (auto *subscript = dyn_cast<SubscriptDecl>(decl)) {
5636
+ paramList = subscript->getIndices ();
5637
+ } else if (auto *enumElement = dyn_cast<EnumElementDecl>(decl)) {
5638
+ paramList = enumElement->getParameterList ();
5639
+ }
5640
+
5641
+ if (paramList) {
5642
+ diag.highlight (paramList->get (paramIdx)->getLoc ());
5643
+ diag.highlight (paramList->get (*matchingBackwardParamIdx)->getLoc ());
5644
+ }
5645
+ };
5646
+
5647
+ // If the parameters have the same name, provide a custom diagnostic and then
5648
+ // bail out early; there are no useful notes we can provide here.
5649
+ if (paramName == backwardParamName) {
5650
+ ctx.Diags .diagnose (trailingClosure->getStartLoc (), diag::unlabeled_trailing_closure_changed_behavior_same_param_name,
5651
+ paramName);
5652
+ noteCallee ();
5653
+ return ;
5654
+ }
5655
+
5656
+ // Produce the diagnostic.
5657
+ ctx.Diags .diagnose (trailingClosure->getStartLoc (), diag::unlabeled_trailing_closure_changed_behavior, paramName,
5658
+ backwardParamName);
5659
+
5660
+ // Produce a note with a Fix-It describing how to resolve the ambiguity.
5661
+ auto diagResolution = [&](Identifier paramName, unsigned which) {
5662
+ // Emit the note.
5663
+ auto diag = ctx.Diags .diagnose (
5664
+ trailingClosure->getStartLoc (), diag::trailing_closure_select_parameter,
5665
+ paramName, which);
5666
+
5667
+ // Figure out the text to be inserted before the trailing closure.
5668
+ SmallString<16 > insertionText;
5669
+ SourceLoc insertionLoc;
5670
+ if (leadingCommaLoc.isValid ()) {
5671
+ insertionText += " , " ;
5672
+ assert (existingRParenLoc.isValid ());
5673
+ insertionLoc = leadingCommaLoc;
5674
+ } else if (existingRParenLoc.isInvalid ()) {
5675
+ insertionText += " (" ;
5676
+ insertionLoc = Lexer::getLocForEndOfToken (
5677
+ ctx.SourceMgr , fn->getEndLoc ());
5678
+ } else {
5679
+ insertionLoc = existingRParenLoc;
5680
+ }
5681
+
5682
+ // Add the label, if there is one.
5683
+ if (!paramName.empty ()) {
5684
+ insertionText += paramName.str ();
5685
+ insertionText += " : " ;
5686
+ }
5687
+
5688
+ // If there is an existing right parentheses, remove it while we
5689
+ // insert the new text.
5690
+ if (existingRParenLoc.isValid ()) {
5691
+ SourceLoc afterExistingRParenLoc = Lexer::getLocForEndOfToken (
5692
+ ctx.SourceMgr , existingRParenLoc);
5693
+ diag.fixItReplaceChars (
5694
+ insertionLoc, afterExistingRParenLoc, insertionText);
5695
+ } else {
5696
+ // Insert the appropriate prefix.
5697
+ diag.fixItInsert (insertionLoc, insertionText);
5698
+ }
5699
+
5700
+
5701
+ // Insert a right parenthesis after the closing '}' of the trailing closure;
5702
+ SourceLoc newRParenLoc = Lexer::getLocForEndOfToken (
5703
+ ctx.SourceMgr , trailingClosure->getEndLoc ());
5704
+ diag.fixItInsert (newRParenLoc, " )" );
5705
+ };
5706
+
5707
+ diagResolution (backwardParamName, 0 );
5708
+ diagResolution (paramName, 1 );
5709
+
5710
+ noteCallee ();
5711
+ }
5712
+
5524
5713
Expr *ExprRewriter::coerceCallArguments (
5525
5714
Expr *arg, AnyFunctionType *funcType,
5526
5715
ConcreteDeclRef callee,
@@ -5594,6 +5783,12 @@ Expr *ExprRewriter::coerceCallArguments(
5594
5783
// FIXME: Eventually, we want to enforce that we have either argTuple or
5595
5784
// argParen here.
5596
5785
5786
+ // Warn if there was a recent change in trailing closure binding semantics
5787
+ // that might have lead to a silent change in behavior.
5788
+ maybeWarnAboutTrailingClosureBindingChange (
5789
+ callee, apply->getFn (), arg, args, params, paramInfo,
5790
+ unlabeledTrailingClosureIndex, parameterBindings);
5791
+
5597
5792
SourceLoc lParenLoc, rParenLoc;
5598
5793
if (argTuple) {
5599
5794
lParenLoc = argTuple->getLParenLoc ();
0 commit comments