@@ -1984,6 +1984,141 @@ static void diagAvailability(TypeChecker &TC, const Expr *E,
1984
1984
const_cast <Expr*>(E)->walk (walker);
1985
1985
}
1986
1986
1987
+ namespace {
1988
+
1989
+ static ClosureExpr* getLastArgAsClosure (Expr* Arg) {
1990
+ if (auto Paren = dyn_cast<ParenExpr>(Arg)) {
1991
+
1992
+ // If the argument is paren expression, get the sub expression as closure.
1993
+ if (!Paren->hasTrailingClosure ())
1994
+ return dyn_cast_or_null<ClosureExpr>(Paren->getSubExpr ());
1995
+ } else if (auto Tuple = dyn_cast<TupleExpr>(Arg)) {
1996
+
1997
+ // If the argument is tuple expression, get the last expression in the tuple
1998
+ // as closure.
1999
+ if (!Tuple->hasTrailingClosure () && Tuple->getNumElements () > 0 )
2000
+ return dyn_cast<ClosureExpr>(Tuple->getElement (Tuple->getNumElements () - 1 ));
2001
+ }
2002
+ return nullptr ;
2003
+ }
2004
+
2005
+ static bool areIdentifiersSame (Identifier Left, Identifier Right) {
2006
+
2007
+ // If both are anonymous, returns true.
2008
+ if (Left.empty () && Right.empty ())
2009
+ return true ;
2010
+
2011
+ // If one is anonymous and the other is not, returns false.
2012
+ if (Left.empty () || Right.empty ())
2013
+ return false ;
2014
+
2015
+ // If both are not anonymous, compare the content.
2016
+ return Left.str () == Right.str ();
2017
+ }
2018
+
2019
+ static bool willCauseAmbiguity (TypeChecker &TC, FuncDecl *SelectedFD) {
2020
+ auto SelectedArgNames = SelectedFD->getEffectiveFullName ().getArgumentNames ();
2021
+ auto SelectedArgNameExcludingClosure = SelectedArgNames.slice (0 ,
2022
+ SelectedArgNames.size () - 1 );
2023
+
2024
+ // Consider all decls from the same context with the simple
2025
+ // identifier with SelectedFD.
2026
+ for (ValueDecl *VD : TC.lookupUnqualified (SelectedFD->getDeclContext (),
2027
+ SelectedFD->getName (), SourceLoc ())) {
2028
+
2029
+ // Make sure the overload is not the selected function.
2030
+ if (VD == SelectedFD)
2031
+ continue ;
2032
+
2033
+ // Make sure the overload is a function.
2034
+ FuncDecl *FD = dyn_cast<FuncDecl>(VD);
2035
+ if (!FD)
2036
+ continue ;
2037
+ auto ArgNames = FD->getEffectiveFullName ().getArgumentNames ();
2038
+
2039
+ // If the overload requires more arguments than the selected; there are no
2040
+ // chance of conflicts.
2041
+ if (ArgNames.size () < SelectedArgNames.size ())
2042
+ continue ;
2043
+
2044
+ // Assuming there is a conflict.
2045
+ bool Conflict = true ;
2046
+ for (unsigned I = 0 ; I < SelectedArgNameExcludingClosure.size (); I ++) {
2047
+ if (!areIdentifiersSame (ArgNames[I], SelectedArgNameExcludingClosure[I])) {
2048
+ // Different argument names disprove our assumption.
2049
+ Conflict = false ;
2050
+ }
2051
+ }
2052
+ if (Conflict)
2053
+ return true ;
2054
+ }
2055
+ return false ;
2056
+ }
2057
+
2058
+ static FuncDecl* digForFuncDecl (Expr *Fn) {
2059
+ auto FD = dyn_cast_or_null<FuncDecl>(Fn->getReferencedDecl ().getDecl ());
2060
+ if (FD)
2061
+ return FD;
2062
+ if (auto DE = dyn_cast<DotSyntaxCallExpr>(Fn)) {
2063
+ return digForFuncDecl (DE->getFn ());
2064
+ }
2065
+ return nullptr ;
2066
+ }
2067
+
2068
+ static void
2069
+ applyConvertToTrailingClosureFixit (TypeChecker &TC, SourceManager &SM,
2070
+ ClosureExpr *Closure, Expr *Arg) {
2071
+ if (auto *Paren = dyn_cast<ParenExpr>(Arg)) {
2072
+ SourceRange LRemove (Paren->getLParenLoc (),
2073
+ Paren->getLParenLoc ().getAdvancedLoc (1 ));
2074
+ SourceRange RRemove (Paren->getRParenLoc (),
2075
+ Paren->getRParenLoc ().getAdvancedLoc (1 ));
2076
+
2077
+ // Remove the left and right paren.
2078
+ TC.diagnose (Closure->getStartLoc (), diag::convert_to_trailing_closure).
2079
+ fixItRemove (LRemove).fixItRemove (RRemove);
2080
+ } else if (auto *Tuple = dyn_cast<TupleExpr>(Arg)) {
2081
+ if (Tuple->getNumElements () > 1 ) {
2082
+ SourceLoc ReplaceStart = Lexer::getLocForEndOfToken (SM,
2083
+ Tuple->getElement (Tuple->getNumElements () - 2 )->getEndLoc ());
2084
+ SourceRange Replace (ReplaceStart, Closure->getStartLoc ());
2085
+ SourceRange ToRemove (Tuple->getRParenLoc (),
2086
+ Tuple->getRParenLoc ().getAdvancedLoc (1 ));
2087
+
2088
+ // Closing the tuple before the closure; and remove the right paren after the
2089
+ // closure.
2090
+ TC.diagnose (Closure->getStartLoc (), diag::convert_to_trailing_closure).
2091
+ fixItReplace (Replace, " ) " ).fixItRemove (ToRemove);
2092
+ } else {
2093
+ SourceRange LRemove (Tuple->getLParenLoc (), Closure->getStartLoc ());
2094
+ SourceRange RRemove (Lexer::getLocForEndOfToken (SM,Closure->getEndLoc ()),
2095
+ Lexer::getLocForEndOfToken (SM, Tuple->getEndLoc ()));
2096
+ TC.diagnose (Closure->getStartLoc (), diag::convert_to_trailing_closure).
2097
+ fixItRemove (LRemove).fixItRemove (RRemove);
2098
+ }
2099
+ }
2100
+ }
2101
+ }
2102
+
2103
+ static void diagConvertToTrailingClosure (TypeChecker &TC, const Expr *E,
2104
+ DeclContext *DC) {
2105
+ auto * AE = dyn_cast<ApplyExpr>(E);
2106
+ if (!AE)
2107
+ return ;
2108
+
2109
+ auto * Closure = getLastArgAsClosure (AE->getArg ());
2110
+ if (!Closure)
2111
+ return ;
2112
+ if (auto * FD = digForFuncDecl (AE->getFn ())) {
2113
+ // Check the condition whether converting will cause ambiguity.
2114
+ if (!willCauseAmbiguity (TC, FD))
2115
+ // Without ambiguity, we can proceed to fix.
2116
+ applyConvertToTrailingClosureFixit (TC, DC->getASTContext ().SourceMgr ,
2117
+ Closure, AE->getArg ());
2118
+ }
2119
+ }
2120
+
2121
+
1987
2122
// ===----------------------------------------------------------------------===//
1988
2123
// Per func/init diagnostics
1989
2124
// ===----------------------------------------------------------------------===//
@@ -3210,6 +3345,7 @@ void swift::performSyntacticExprDiagnostics(TypeChecker &TC, const Expr *E,
3210
3345
diagRecursivePropertyAccess (TC, E, DC);
3211
3346
diagnoseImplicitSelfUseInClosure (TC, E, DC);
3212
3347
diagAvailability (TC, E, const_cast <DeclContext*>(DC));
3348
+ diagConvertToTrailingClosure (TC, E, const_cast <DeclContext*>(DC));
3213
3349
if (TC.Context .LangOpts .EnableObjCInterop )
3214
3350
diagDeprecatedObjCSelectors (TC, DC, E);
3215
3351
}
0 commit comments