Skip to content

Commit 24c212d

Browse files
committed
Correct renaming fix-its for trailing closures, disable them for operators (#2626)
Merge pull request #2626 from jrose-apple/trailing-closure-rename-fixits
2 parents b2aaa61 + 5343b59 commit 24c212d

File tree

5 files changed

+98
-34
lines changed

5 files changed

+98
-34
lines changed

lib/Sema/MiscDiagnostics.cpp

Lines changed: 46 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1006,36 +1006,48 @@ void swift::fixItAvailableAttrRename(TypeChecker &TC,
10061006
InFlightDiagnostic &diag,
10071007
SourceRange referenceRange,
10081008
const AvailableAttr *attr,
1009-
const CallExpr *CE) {
1009+
const ApplyExpr *call) {
10101010
ParsedDeclName parsed = swift::parseDeclName(attr->Rename);
10111011
if (!parsed)
10121012
return;
10131013

1014+
bool originallyWasKnownOperatorExpr = false;
1015+
if (call) {
1016+
originallyWasKnownOperatorExpr =
1017+
isa<BinaryExpr>(call) ||
1018+
isa<PrefixUnaryExpr>(call) ||
1019+
isa<PostfixUnaryExpr>(call);
1020+
}
1021+
if (parsed.isOperator() != originallyWasKnownOperatorExpr)
1022+
return;
1023+
10141024
SourceManager &sourceMgr = TC.Context.SourceMgr;
10151025

10161026
if (parsed.isInstanceMember()) {
10171027
// Replace the base of the call with the "self argument".
10181028
// We can only do a good job with the fix-it if we have the whole call
10191029
// expression.
10201030
// FIXME: Should we be validating the ContextName in some way?
1021-
if (!CE)
1031+
if (!dyn_cast_or_null<CallExpr>(call))
10221032
return;
10231033

10241034
unsigned selfIndex = parsed.SelfIndex.getValue();
10251035
const Expr *selfExpr = nullptr;
10261036
SourceLoc removeRangeStart;
10271037
SourceLoc removeRangeEnd;
10281038

1029-
const Expr *argExpr = CE->getArg();
1039+
const Expr *argExpr = call->getArg();
10301040
if (auto args = dyn_cast<TupleExpr>(argExpr)) {
1031-
if (selfIndex >= args->getNumElements())
1041+
size_t numElementsWithinParens = args->getNumElements();
1042+
numElementsWithinParens -= args->hasTrailingClosure();
1043+
if (selfIndex >= numElementsWithinParens)
10321044
return;
10331045

10341046
if (parsed.IsGetter) {
1035-
if (args->getNumElements() != 1)
1047+
if (numElementsWithinParens != 1)
10361048
return;
10371049
} else if (parsed.IsSetter) {
1038-
if (args->getNumElements() != 2)
1050+
if (numElementsWithinParens != 2)
10391051
return;
10401052
} else {
10411053
if (parsed.ArgumentLabels.size() != args->getNumElements() - 1)
@@ -1044,7 +1056,7 @@ void swift::fixItAvailableAttrRename(TypeChecker &TC,
10441056

10451057
selfExpr = args->getElement(selfIndex);
10461058

1047-
if (selfIndex + 1 == args->getNumElements()) {
1059+
if (selfIndex + 1 == numElementsWithinParens) {
10481060
if (selfIndex > 0) {
10491061
// Remove from the previous comma to the close-paren (half-open).
10501062
removeRangeStart = args->getElement(selfIndex-1)->getEndLoc();
@@ -1055,7 +1067,12 @@ void swift::fixItAvailableAttrRename(TypeChecker &TC,
10551067
removeRangeStart = Lexer::getLocForEndOfToken(sourceMgr,
10561068
argExpr->getStartLoc());
10571069
}
1058-
removeRangeEnd = args->getEndLoc();
1070+
1071+
// Prefer the r-paren location, so that we get the right behavior when
1072+
// there's a trailing closure, but handle some implicit cases too.
1073+
removeRangeEnd = args->getRParenLoc();
1074+
if (removeRangeEnd.isInvalid())
1075+
removeRangeEnd = args->getEndLoc();
10591076

10601077
} else {
10611078
// Remove from the label to the start of the next argument (half-open).
@@ -1108,21 +1125,22 @@ void swift::fixItAvailableAttrRename(TypeChecker &TC,
11081125
selfReplace.push_back(')');
11091126
selfReplace.push_back('.');
11101127
selfReplace += parsed.BaseName;
1111-
diag.fixItReplace(CE->getFn()->getSourceRange(), selfReplace);
1128+
diag.fixItReplace(call->getFn()->getSourceRange(), selfReplace);
11121129

11131130
if (!parsed.isPropertyAccessor())
11141131
diag.fixItRemoveChars(removeRangeStart, removeRangeEnd);
11151132

11161133
// Continue on to diagnose any argument label renames.
11171134

1118-
} else if (parsed.BaseName == TC.Context.Id_init.str() && CE) {
1135+
} else if (parsed.BaseName == TC.Context.Id_init.str() &&
1136+
dyn_cast_or_null<CallExpr>(call)) {
11191137
// For initializers, replace with a "call" of the context type...but only
11201138
// if we know we're doing a call (rather than a first-class reference).
11211139
if (parsed.isMember()) {
1122-
diag.fixItReplace(CE->getFn()->getSourceRange(), parsed.ContextName);
1140+
diag.fixItReplace(call->getFn()->getSourceRange(), parsed.ContextName);
11231141

11241142
} else {
1125-
auto *dotCall = dyn_cast<DotSyntaxCallExpr>(CE->getFn());
1143+
auto *dotCall = dyn_cast<DotSyntaxCallExpr>(call->getFn());
11261144
if (!dotCall)
11271145
return;
11281146

@@ -1144,10 +1162,10 @@ void swift::fixItAvailableAttrRename(TypeChecker &TC,
11441162
diag.fixItReplace(referenceRange, baseReplace);
11451163
}
11461164

1147-
if (!CE)
1165+
if (!dyn_cast_or_null<CallExpr>(call))
11481166
return;
11491167

1150-
const Expr *argExpr = CE->getArg();
1168+
const Expr *argExpr = call->getArg();
11511169
if (parsed.IsGetter) {
11521170
diag.fixItRemove(argExpr->getSourceRange());
11531171
return;
@@ -1290,7 +1308,7 @@ void TypeChecker::diagnoseDeprecated(SourceRange ReferenceRange,
12901308
const DeclContext *ReferenceDC,
12911309
const AvailableAttr *Attr,
12921310
DeclName Name,
1293-
const CallExpr *CE) {
1311+
const ApplyExpr *Call) {
12941312
// We match the behavior of clang to not report deprecation warnings
12951313
// inside declarations that are themselves deprecated on all deployment
12961314
// targets.
@@ -1347,7 +1365,7 @@ void TypeChecker::diagnoseDeprecated(SourceRange ReferenceRange,
13471365
auto renameDiag = diagnose(ReferenceRange.Start,
13481366
diag::note_deprecated_rename,
13491367
newName);
1350-
fixItAvailableAttrRename(*this, renameDiag, ReferenceRange, Attr, CE);
1368+
fixItAvailableAttrRename(*this, renameDiag, ReferenceRange, Attr, Call);
13511369
}
13521370
}
13531371

@@ -1400,11 +1418,11 @@ void TypeChecker::diagnoseUnavailableOverride(ValueDecl *override,
14001418
bool TypeChecker::diagnoseExplicitUnavailability(const ValueDecl *D,
14011419
SourceRange R,
14021420
const DeclContext *DC,
1403-
const CallExpr *CE) {
1421+
const ApplyExpr *call) {
14041422
return diagnoseExplicitUnavailability(D, R, DC,
14051423
[=](InFlightDiagnostic &diag) {
14061424
fixItAvailableAttrRename(*this, diag, R, AvailableAttr::isUnavailable(D),
1407-
CE);
1425+
call);
14081426
});
14091427
}
14101428

@@ -1539,19 +1557,19 @@ class AvailabilityWalker : public ASTWalker {
15391557

15401558
if (auto DR = dyn_cast<DeclRefExpr>(E))
15411559
diagAvailability(DR->getDecl(), DR->getSourceRange(),
1542-
getEnclosingCallExpr());
1560+
getEnclosingApplyExpr());
15431561
if (auto MR = dyn_cast<MemberRefExpr>(E)) {
15441562
walkMemberRef(MR);
15451563
return skipChildren();
15461564
}
15471565
if (auto OCDR = dyn_cast<OtherConstructorDeclRefExpr>(E))
15481566
diagAvailability(OCDR->getDecl(),
15491567
OCDR->getConstructorLoc().getSourceRange(),
1550-
getEnclosingCallExpr());
1568+
getEnclosingApplyExpr());
15511569
if (auto DMR = dyn_cast<DynamicMemberRefExpr>(E))
15521570
diagAvailability(DMR->getMember().getDecl(),
15531571
DMR->getNameLoc().getSourceRange(),
1554-
getEnclosingCallExpr());
1572+
getEnclosingApplyExpr());
15551573
if (auto DS = dyn_cast<DynamicSubscriptExpr>(E))
15561574
diagAvailability(DS->getMember().getDecl(), DS->getSourceRange());
15571575
if (auto S = dyn_cast<SubscriptExpr>(E)) {
@@ -1579,12 +1597,12 @@ class AvailabilityWalker : public ASTWalker {
15791597

15801598
private:
15811599
bool diagAvailability(const ValueDecl *D, SourceRange R,
1582-
const CallExpr *CE = nullptr);
1600+
const ApplyExpr *call = nullptr);
15831601
bool diagnoseIncDecRemoval(const ValueDecl *D, SourceRange R,
15841602
const AvailableAttr *Attr);
15851603

1586-
/// Walks up from a potential callee to the enclosing CallExpr.
1587-
const CallExpr *getEnclosingCallExpr() const {
1604+
/// Walks up from a potential callee to the enclosing ApplyExpr.
1605+
const ApplyExpr *getEnclosingApplyExpr() const {
15881606
ArrayRef<const Expr *> parents = ExprStack;
15891607
assert(!parents.empty() && "must be called while visiting an expression");
15901608
size_t idx = parents.size() - 1;
@@ -1599,7 +1617,7 @@ class AvailabilityWalker : public ASTWalker {
15991617
isa<ForceValueExpr>(parents[idx]) || // f!(a)
16001618
isa<BindOptionalExpr>(parents[idx])); // f?(a)
16011619

1602-
auto *call = dyn_cast<CallExpr>(parents[idx]);
1620+
auto *call = dyn_cast<ApplyExpr>(parents[idx]);
16031621
if (!call || call->getFn() != parents[idx+1])
16041622
return nullptr;
16051623
return call;
@@ -1716,20 +1734,20 @@ class AvailabilityWalker : public ASTWalker {
17161734
/// Diagnose uses of unavailable declarations. Returns true if a diagnostic
17171735
/// was emitted.
17181736
bool AvailabilityWalker::diagAvailability(const ValueDecl *D, SourceRange R,
1719-
const CallExpr *CE) {
1737+
const ApplyExpr *call) {
17201738
if (!D)
17211739
return false;
17221740

17231741
if (auto *attr = AvailableAttr::isUnavailable(D))
17241742
if (diagnoseIncDecRemoval(D, R, attr))
17251743
return true;
17261744

1727-
if (TC.diagnoseExplicitUnavailability(D, R, DC, CE))
1745+
if (TC.diagnoseExplicitUnavailability(D, R, DC, call))
17281746
return true;
17291747

17301748
// Diagnose for deprecation
17311749
if (const AvailableAttr *Attr = TypeChecker::getDeprecated(D)) {
1732-
TC.diagnoseDeprecated(R, DC, Attr, D->getFullName(), CE);
1750+
TC.diagnoseDeprecated(R, DC, Attr, D->getFullName(), call);
17331751
}
17341752

17351753
if (TC.getLangOpts().DisableAvailabilityChecking)

lib/Sema/MiscDiagnostics.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@
2222

2323
namespace swift {
2424
class AbstractFunctionDecl;
25+
class ApplyExpr;
2526
class AvailableAttr;
26-
class CallExpr;
2727
class DeclContext;
2828
class Expr;
2929
class InFlightDiagnostic;
@@ -61,13 +61,13 @@ bool diagnoseArgumentLabelError(TypeChecker &TC, const Expr *expr,
6161
InFlightDiagnostic *existingDiag = nullptr);
6262

6363
/// Emit fix-its to rename the base name at \p referenceRange based on the
64-
/// "renamed" argument in \p attr. If \p CE is provided, the argument labels
64+
/// "renamed" argument in \p attr. If \p call is provided, the argument labels
6565
/// will also be updated.
6666
void fixItAvailableAttrRename(TypeChecker &TC,
6767
InFlightDiagnostic &diag,
6868
SourceRange referenceRange,
6969
const AvailableAttr *attr,
70-
const CallExpr *CE);
70+
const ApplyExpr *call);
7171
} // namespace swift
7272

7373
#endif // SWIFT_SEMA_MISC_DIAGNOSTICS_H

lib/Sema/TypeChecker.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1616,7 +1616,7 @@ class TypeChecker final : public LazyResolver {
16161616
bool diagnoseExplicitUnavailability(const ValueDecl *D,
16171617
SourceRange R,
16181618
const DeclContext *DC,
1619-
const CallExpr *CE);
1619+
const ApplyExpr *call);
16201620

16211621
/// Emit a diagnostic for references to declarations that have been
16221622
/// marked as unavailable, either through "unavailable" or "obsoleted:".
@@ -1829,7 +1829,7 @@ class TypeChecker final : public LazyResolver {
18291829
const DeclContext *ReferenceDC,
18301830
const AvailableAttr *Attr,
18311831
DeclName Name,
1832-
const CallExpr *CE);
1832+
const ApplyExpr *Call);
18331833
/// @}
18341834

18351835
/// If LangOptions::DebugForbidTypecheckPrefix is set and the given decl

test/ClangModules/attr-swift_name_renaming.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ func test() {
3737
// This particular instance method mapping previously caused a crash because
3838
// of the trailing closure.
3939
acceptsClosure(Foo(), test) // expected-error {{'acceptsClosure' has been replaced by instance method 'Foo.accepts(closure:)'}} {{3-17=(Foo()).accepts}} {{18-25=}} {{25-25=closure: }}
40-
acceptsClosure(Foo()) {} // expected-error {{'acceptsClosure' has been replaced by instance method 'Foo.accepts(closure:)'}} {{3-17=(Foo()).accepts}} {{18-25=}}
40+
acceptsClosure(Foo()) {} // expected-error {{'acceptsClosure' has been replaced by instance method 'Foo.accepts(closure:)'}} {{3-17=(Foo()).accepts}} {{18-23=}}
4141

4242
Foo().accepts(closure: test)
4343
Foo().accepts() {}

test/attr/attr_availability.swift

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,52 @@ func testRenameSetters() {
479479
unavailableSetInstancePropertyInout(a: &x, b: 2) // expected-error{{'unavailableSetInstancePropertyInout(a:b:)' has been replaced by property 'Int.prop'}} {{3-38=x.prop}} {{38-49= = }} {{50-51=}}
480480
}
481481
482+
@available(*, unavailable, renamed: "Int.foo(self:execute:)")
483+
func trailingClosure(_ value: Int, fn: () -> Void) {} // expected-note {{here}}
484+
@available(*, unavailable, renamed: "Int.foo(self:bar:execute:)")
485+
func trailingClosureArg(_ value: Int, _ other: Int, fn: () -> Void) {} // expected-note {{here}}
486+
@available(*, unavailable, renamed: "Int.foo(bar:self:execute:)")
487+
func trailingClosureArg2(_ value: Int, _ other: Int, fn: () -> Void) {} // expected-note {{here}}
488+
489+
func testInstanceTrailingClosure() {
490+
trailingClosure(0) {} // expected-error {{'trailingClosure(_:fn:)' has been replaced by instance method 'Int.foo(execute:)'}} {{3-18=0.foo}} {{19-20=}}
491+
trailingClosureArg(0, 1) {} // expected-error {{'trailingClosureArg(_:_:fn:)' has been replaced by instance method 'Int.foo(bar:execute:)'}} {{3-21=0.foo}} {{22-25=}} {{25-25=bar: }}
492+
trailingClosureArg2(0, 1) {} // expected-error {{'trailingClosureArg2(_:_:fn:)' has been replaced by instance method 'Int.foo(bar:execute:)'}} {{3-22=1.foo}} {{23-23=bar: }} {{24-27=}}
493+
}
494+
495+
@available(*, unavailable, renamed: "+")
496+
func add(_ value: Int, _ other: Int) {} // expected-note {{here}}
497+
498+
infix operator *** {}
499+
@available(*, unavailable, renamed: "add")
500+
func ***(value: (), other: ()) {} // expected-note {{here}}
501+
@available(*, unavailable, renamed: "Int.foo(self:_:)")
502+
func ***(value: Int, other: Int) {} // expected-note {{here}}
503+
504+
prefix operator *** {}
505+
@available(*, unavailable, renamed: "add")
506+
prefix func ***(value: Int?) {} // expected-note {{here}}
507+
@available(*, unavailable, renamed: "Int.foo(self:)")
508+
prefix func ***(value: Int) {} // expected-note {{here}}
509+
510+
postfix operator *** {}
511+
@available(*, unavailable, renamed: "add")
512+
postfix func ***(value: Int?) {} // expected-note {{here}}
513+
@available(*, unavailable, renamed: "Int.foo(self:)")
514+
postfix func ***(value: Int) {} // expected-note {{here}}
515+
516+
func testOperators() {
517+
add(0, 1) // expected-error {{'add' has been renamed to '+'}} {{none}}
518+
() *** () // expected-error {{'***' has been renamed to 'add'}} {{none}}
519+
0 *** 1 // expected-error {{'***' has been replaced by instance method 'Int.foo(_:)'}} {{none}}
520+
521+
***nil // expected-error {{'***' has been renamed to 'add'}} {{none}}
522+
***0 // expected-error {{'***' has been replaced by instance method 'Int.foo()'}} {{none}}
523+
524+
nil*** // expected-error {{'***' has been renamed to 'add'}} {{none}}
525+
0*** // expected-error {{'***' has been replaced by instance method 'Int.foo()'}} {{none}}
526+
}
527+
482528
extension Int {
483529
@available(*, unavailable, renamed: "init(other:)")
484530
@discardableResult

0 commit comments

Comments
 (0)