@@ -367,8 +367,179 @@ struct SyntacticMigratorPass::Implementation : public SourceEntityWalker {
367
367
}
368
368
}
369
369
370
+ // / Migrates code that compiles fine in Swift 3 but breaks in Swift 4 due to
371
+ // / changes in how the typechecker handles tuple arguments.
372
+ void handleTupleArgumentMismatches (const CallExpr *E) {
373
+ if (!SF->getASTContext ().LangOpts .isSwiftVersion3 ())
374
+ return ;
375
+ if (E->isImplicit ())
376
+ return ;
377
+
378
+ // Handles such kind of cases:
379
+ // \code
380
+ // func test(_: ()) {}
381
+ // test()
382
+ // \endcode
383
+ // This compiles fine in Swift 3 but Swift 4 complains with
384
+ // error: missing argument for parameter #1 in call
385
+ //
386
+ // It will fix the code to "test(())".
387
+ //
388
+ auto handleCallsToEmptyTuple = [&](const CallExpr *E) -> bool {
389
+ auto fnTy = E->getFn ()->getType ()->getAs <FunctionType>();
390
+ if (!fnTy)
391
+ return false ;
392
+ auto parenT = dyn_cast<ParenType>(fnTy->getInput ().getPointer ());
393
+ if (!parenT)
394
+ return false ;
395
+ auto inp = dyn_cast<TupleType>(parenT->getUnderlyingType ().getPointer ());
396
+ if (!inp)
397
+ return false ;
398
+ if (inp->getNumElements () != 0 )
399
+ return false ;
400
+ auto argTupleT = dyn_cast<TupleType>(E->getArg ()->getType ().getPointer ());
401
+ if (!argTupleT)
402
+ return false ;
403
+ if (argTupleT->getNumElements () != 0 )
404
+ return false ;
405
+ Editor.insertWrap (" (" , E->getArg ()->getSourceRange (), " )" );
406
+ return true ;
407
+ };
408
+
409
+ // Handles such kind of cases:
410
+ // \code
411
+ // func test(_: ((Int, Int)) -> ()) {}
412
+ // test({ (x,y) in })
413
+ // \endcode
414
+ // This compiles fine in Swift 3 but Swift 4 complains with
415
+ // error: cannot convert value of type '(_, _) -> ()' to expected argument type '((Int, Int)) -> ()'
416
+ //
417
+ // It will fix the code to "test({ let (x,y) = $0; })".
418
+ //
419
+ auto handleTupleMapToClosureArgs = [&](const CallExpr *E) -> bool {
420
+ auto fnTy = E->getFn ()->getType ()->getAs <FunctionType>();
421
+ if (!fnTy)
422
+ return false ;
423
+ auto fnTy2 = fnTy->getInput ()->getAs <FunctionType>();
424
+ if (!fnTy2)
425
+ return false ;
426
+ auto parenT = dyn_cast<ParenType>(fnTy2->getInput ().getPointer ());
427
+ if (!parenT)
428
+ return false ;
429
+ auto tupleInFn = dyn_cast<TupleType>(parenT->getUnderlyingType ().getPointer ());
430
+ if (!tupleInFn)
431
+ return false ;
432
+ if (!E->getArg ())
433
+ return false ;
434
+ auto argE = E->getArg ()->getSemanticsProvidingExpr ();
435
+ while (auto *ICE = dyn_cast<ImplicitConversionExpr>(argE))
436
+ argE = ICE->getSubExpr ();
437
+ argE = argE->getSemanticsProvidingExpr ();
438
+ auto closureE = dyn_cast<ClosureExpr>(argE);
439
+ if (!closureE)
440
+ return false ;
441
+ if (closureE->getInLoc ().isInvalid ())
442
+ return false ;
443
+ auto paramList = closureE->getParameters ();
444
+ if (!paramList ||
445
+ paramList->getLParenLoc ().isInvalid () || paramList->getRParenLoc ().isInvalid ())
446
+ return false ;
447
+ if (paramList->size () != tupleInFn->getNumElements ())
448
+ return false ;
449
+ if (paramList->size () == 0 )
450
+ return false ;
451
+
452
+ auto hasParamListWithNoTypes = [&]() {
453
+ if (closureE->hasExplicitResultType ())
454
+ return false ;
455
+ for (auto *param : *paramList) {
456
+ auto tyLoc = param->getTypeLoc ();
457
+ if (!tyLoc.isNull ())
458
+ return false ;
459
+ }
460
+ return true ;
461
+ };
462
+
463
+ if (hasParamListWithNoTypes ()) {
464
+ // Simpler form depending on type inference.
465
+ // Change "(x, y) in " to "let (x, y) = $0;".
466
+
467
+ Editor.insert (paramList->getLParenLoc (), " let " );
468
+ for (auto *param : *paramList) {
469
+ // If the argument list is like "(_ x, _ y)", remove the underscores.
470
+ if (param->getArgumentNameLoc ().isValid ()) {
471
+ Editor.remove (CharSourceRange (SM, param->getArgumentNameLoc (),
472
+ param->getNameLoc ()));
473
+ }
474
+ // If the argument list has type annotations, remove them.
475
+ auto tyLoc = param->getTypeLoc ();
476
+ if (!tyLoc.isNull () && !tyLoc.getSourceRange ().isInvalid ()) {
477
+ auto nameRange = CharSourceRange (param->getNameLoc (),
478
+ param->getNameStr ().size ());
479
+ auto tyRange = Lexer::getCharSourceRangeFromSourceRange (SM,
480
+ tyLoc.getSourceRange ());
481
+ Editor.remove (CharSourceRange (SM, nameRange.getEnd (),
482
+ tyRange.getEnd ()));
483
+ }
484
+ }
485
+
486
+ Editor.replaceToken (closureE->getInLoc (), " = $0;" );
487
+ return true ;
488
+ }
489
+
490
+ // Includes types in the closure signature. The following will do a
491
+ // more complicated edit than the above:
492
+ // (x: Int, y: Int) -> Int in
493
+ // to
494
+ // (__val:(Int, Int)) -> Int in let (x,y) = __val;
495
+
496
+ std::string paramListText;
497
+ {
498
+ llvm::raw_string_ostream OS (paramListText);
499
+ OS << " (__val:(" ;
500
+ for (size_t i = 0 , e = paramList->size (); i != e; ++i) {
501
+ if (i != 0 )
502
+ OS << " , " ;
503
+ auto param = paramList->get (i);
504
+ auto tyLoc = param->getTypeLoc ();
505
+ if (!tyLoc.isNull () && !tyLoc.getSourceRange ().isInvalid ()) {
506
+ OS << SM.extractText (
507
+ Lexer::getCharSourceRangeFromSourceRange (SM,
508
+ tyLoc.getSourceRange ()));
509
+ } else {
510
+ param->getType ().print (OS);
511
+ }
512
+ }
513
+ OS << " ))" ;
514
+ }
515
+ std::string varBindText;
516
+ {
517
+ llvm::raw_string_ostream OS (varBindText);
518
+ OS << " let (" ;
519
+ for (size_t i = 0 , e = paramList->size (); i != e; ++i) {
520
+ if (i != 0 )
521
+ OS << " ," ;
522
+ auto param = paramList->get (i);
523
+ OS << param->getNameStr ();
524
+ }
525
+ OS << " ) = __val; " ;
526
+ }
527
+
528
+ Editor.replace (paramList->getSourceRange (), paramListText);
529
+ Editor.insertAfterToken (closureE->getInLoc (), varBindText);
530
+ return true ;
531
+ };
532
+
533
+ if (handleCallsToEmptyTuple (E))
534
+ return ;
535
+ if (handleTupleMapToClosureArgs (E))
536
+ return ;
537
+ }
538
+
370
539
bool walkToExprPre (Expr *E) override {
371
540
if (auto *CE = dyn_cast<CallExpr>(E)) {
541
+ handleTupleArgumentMismatches (CE);
542
+
372
543
auto Fn = CE->getFn ();
373
544
auto Args = CE->getArg ();
374
545
switch (Fn->getKind ()) {
0 commit comments