Skip to content

Commit 9c8cfb3

Browse files
authored
Merge pull request #39091 from mininny/fix-rename-subscript
[Diagnostics] Fix diagnostics to produce valid rename fixit for SubscriptExpr
2 parents 6c74c0f + 3e8b076 commit 9c8cfb3

File tree

6 files changed

+138
-50
lines changed

6 files changed

+138
-50
lines changed

include/swift/Parse/Parser.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1869,6 +1869,8 @@ struct ParsedDeclName {
18691869
/// Whether this is a setter for the named property.
18701870
bool IsSetter = false;
18711871

1872+
bool IsSubscript = false;
1873+
18721874
/// For a declaration name that makes the declaration into an
18731875
/// instance member, the index of the "Self" parameter.
18741876
Optional<unsigned> SelfIndex;

lib/Parse/Parser.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1405,6 +1405,11 @@ ParsedDeclName swift::parseDeclName(StringRef name) {
14051405
baseName = baseName.substr(7);
14061406
}
14071407

1408+
// If the base name is prefixed by "subscript", it's an subscript.
1409+
if (baseName == "subscript") {
1410+
result.IsSubscript = true;
1411+
}
1412+
14081413
// Parse the base name.
14091414
if (parseBaseName(baseName)) return ParsedDeclName();
14101415

lib/Sema/TypeCheckAvailability.cpp

Lines changed: 73 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1855,7 +1855,7 @@ static void fixItAvailableAttrRename(InFlightDiagnostic &diag,
18551855
SourceRange referenceRange,
18561856
const ValueDecl *renamedDecl,
18571857
const AvailableAttr *attr,
1858-
const ApplyExpr *call) {
1858+
const Expr *call) {
18591859
if (isa<AccessorDecl>(renamedDecl))
18601860
return;
18611861

@@ -1875,21 +1875,21 @@ static void fixItAvailableAttrRename(InFlightDiagnostic &diag,
18751875

18761876
auto &ctx = renamedDecl->getASTContext();
18771877
SourceManager &sourceMgr = ctx.SourceMgr;
1878-
18791878
if (parsed.isInstanceMember()) {
1879+
auto *CE = dyn_cast_or_null<CallExpr>(call);
1880+
if (!CE)
1881+
return;
1882+
18801883
// Replace the base of the call with the "self argument".
18811884
// We can only do a good job with the fix-it if we have the whole call
18821885
// expression.
18831886
// FIXME: Should we be validating the ContextName in some way?
1884-
if (!call || !isa<CallExpr>(call))
1885-
return;
1886-
18871887
unsigned selfIndex = parsed.SelfIndex.getValue();
18881888
const Expr *selfExpr = nullptr;
18891889
SourceLoc removeRangeStart;
18901890
SourceLoc removeRangeEnd;
18911891

1892-
auto *argExpr = call->getArg();
1892+
auto *argExpr = CE->getArg();
18931893
auto argList = getOriginalArgumentList(argExpr);
18941894

18951895
size_t numElementsWithinParens = argList.args.size();
@@ -1986,54 +1986,91 @@ static void fixItAvailableAttrRename(InFlightDiagnostic &diag,
19861986
selfReplace += base;
19871987
if (needsParens)
19881988
selfReplace.push_back(')');
1989+
19891990
selfReplace.push_back('.');
19901991
selfReplace += parsed.BaseName;
1991-
diag.fixItReplace(call->getFn()->getSourceRange(), selfReplace);
1992+
1993+
diag.fixItReplace(CE->getFn()->getSourceRange(), selfReplace);
19921994

19931995
if (!parsed.isPropertyAccessor())
19941996
diag.fixItRemoveChars(removeRangeStart, removeRangeEnd);
19951997

19961998
// Continue on to diagnose any argument label renames.
19971999

19982000
} else if (parsed.BaseName == "init" && isa_and_nonnull<CallExpr>(call)) {
2001+
auto *CE = cast<CallExpr>(call);
2002+
19992003
// For initializers, replace with a "call" of the context type...but only
20002004
// if we know we're doing a call (rather than a first-class reference).
20012005
if (parsed.isMember()) {
2002-
diag.fixItReplace(call->getFn()->getSourceRange(), parsed.ContextName);
2003-
2004-
} else if (auto *dotCall = dyn_cast<DotSyntaxCallExpr>(call->getFn())) {
2006+
diag.fixItReplace(CE->getFn()->getSourceRange(), parsed.ContextName);
2007+
} else if (auto *dotCall = dyn_cast<DotSyntaxCallExpr>(CE->getFn())) {
20052008
SourceLoc removeLoc = dotCall->getDotLoc();
20062009
if (removeLoc.isInvalid())
20072010
return;
20082011

20092012
diag.fixItRemove(SourceRange(removeLoc, dotCall->getFn()->getEndLoc()));
2010-
} else if (!isa<ConstructorRefCallExpr>(call->getFn())) {
2013+
} else if (!isa<ConstructorRefCallExpr>(CE->getFn())) {
20112014
return;
20122015
}
20132016

20142017
// Continue on to diagnose any constructor argument label renames.
2015-
2018+
2019+
} else if (parsed.IsSubscript) {
2020+
if (auto *CE = dyn_cast_or_null<CallExpr>(call)) {
2021+
// Renaming from CallExpr to SubscriptExpr. Remove function name and
2022+
// replace parens with square brackets.
2023+
2024+
if (auto *DSCE = dyn_cast<DotSyntaxCallExpr>(CE->getFn())) {
2025+
if (DSCE->getBase()->isImplicit()) {
2026+
// If self is implicit, self must be inserted before subscript syntax.
2027+
diag.fixItInsert(CE->getStartLoc(), "self");
2028+
}
2029+
}
2030+
2031+
diag.fixItReplace(CE->getFn()->getEndLoc(), "[");
2032+
diag.fixItReplace(CE->getEndLoc(), "]");
2033+
}
20162034
} else {
20172035
// Just replace the base name.
20182036
SmallString<64> baseReplace;
2037+
20192038
if (!parsed.ContextName.empty()) {
20202039
baseReplace += parsed.ContextName;
20212040
baseReplace += '.';
20222041
}
20232042
baseReplace += parsed.BaseName;
2024-
if (parsed.IsFunctionName && parsed.ArgumentLabels.empty() &&
2025-
isa<VarDecl>(renamedDecl)) {
2026-
// If we're going from a var to a function with no arguments, emit an
2027-
// empty parameter list.
2028-
baseReplace += "()";
2043+
2044+
if (parsed.IsFunctionName && isa_and_nonnull<SubscriptExpr>(call)) {
2045+
auto *SE = cast<SubscriptExpr>(call);
2046+
2047+
// Renaming from SubscriptExpr to CallExpr. Insert function name and
2048+
// replace square brackets with parens.
2049+
diag.fixItReplace(SE->getIndex()->getStartLoc(),
2050+
("." + baseReplace.str() + "(").str());
2051+
diag.fixItReplace(SE->getEndLoc(), ")");
2052+
} else {
2053+
if (parsed.IsFunctionName && parsed.ArgumentLabels.empty() &&
2054+
isa<VarDecl>(renamedDecl)) {
2055+
// If we're going from a var to a function with no arguments, emit an
2056+
// empty parameter list.
2057+
baseReplace += "()";
2058+
}
2059+
diag.fixItReplace(referenceRange, baseReplace);
20292060
}
2030-
diag.fixItReplace(referenceRange, baseReplace);
20312061
}
20322062

2033-
if (!call || !isa<CallExpr>(call))
2063+
if (!call)
2064+
return;
2065+
2066+
Expr *argExpr;
2067+
if (auto *CE = dyn_cast<CallExpr>(call))
2068+
argExpr = CE->getArg();
2069+
else if (auto *SE = dyn_cast<SubscriptExpr>(call))
2070+
argExpr = SE->getIndex();
2071+
else
20342072
return;
20352073

2036-
auto *argExpr = call->getArg();
20372074
auto argList = getOriginalArgumentList(argExpr);
20382075

20392076
if (parsed.IsGetter) {
@@ -2146,7 +2183,8 @@ static void fixItAvailableAttrRename(InFlightDiagnostic &diag,
21462183
if (argList.isUnlabeledTrailingClosureIdx(i))
21472184
continue;
21482185
if (argumentLabelIDs[i] != argList.labels[i]) {
2149-
diagnoseArgumentLabelError(ctx, argExpr, argumentLabelIDs, false, &diag);
2186+
diagnoseArgumentLabelError(ctx, argExpr, argumentLabelIDs,
2187+
parsed.IsSubscript, &diag);
21502188
return;
21512189
}
21522190
}
@@ -2189,7 +2227,7 @@ describeRename(ASTContext &ctx, const AvailableAttr *attr, const ValueDecl *D,
21892227
name << parsed.ContextName << '.';
21902228

21912229
if (parsed.IsFunctionName) {
2192-
name << parsed.formDeclName(ctx);
2230+
name << parsed.formDeclName(ctx, (D && isa<SubscriptDecl>(D)));
21932231
} else {
21942232
name << parsed.BaseName;
21952233
}
@@ -2206,7 +2244,7 @@ describeRename(ASTContext &ctx, const AvailableAttr *attr, const ValueDecl *D,
22062244
void TypeChecker::diagnoseIfDeprecated(SourceRange ReferenceRange,
22072245
const ExportContext &Where,
22082246
const ValueDecl *DeprecatedDecl,
2209-
const ApplyExpr *Call) {
2247+
const Expr *Call) {
22102248
const AvailableAttr *Attr = TypeChecker::getDeprecated(DeprecatedDecl);
22112249
if (!Attr)
22122250
return;
@@ -2390,10 +2428,9 @@ void swift::diagnoseUnavailableOverride(ValueDecl *override,
23902428

23912429
/// Emit a diagnostic for references to declarations that have been
23922430
/// marked as unavailable, either through "unavailable" or "obsoleted:".
2393-
bool swift::diagnoseExplicitUnavailability(const ValueDecl *D,
2394-
SourceRange R,
2431+
bool swift::diagnoseExplicitUnavailability(const ValueDecl *D, SourceRange R,
23952432
const ExportContext &Where,
2396-
const ApplyExpr *call,
2433+
const Expr *call,
23972434
DeclAvailabilityFlags Flags) {
23982435
return diagnoseExplicitUnavailability(D, R, Where, Flags,
23992436
[=](InFlightDiagnostic &diag) {
@@ -2760,7 +2797,7 @@ class ExprAvailabilityWalker : public ASTWalker {
27602797
diagnoseDeclRefAvailability(DS->getMember(), DS->getSourceRange());
27612798
if (auto S = dyn_cast<SubscriptExpr>(E)) {
27622799
if (S->hasDecl()) {
2763-
diagnoseDeclRefAvailability(S->getDecl(), S->getSourceRange());
2800+
diagnoseDeclRefAvailability(S->getDecl(), S->getSourceRange(), S);
27642801
maybeDiagStorageAccess(S->getDecl().getDecl(), S->getSourceRange(), DC);
27652802
}
27662803
}
@@ -2818,7 +2855,7 @@ class ExprAvailabilityWalker : public ASTWalker {
28182855
}
28192856

28202857
bool diagnoseDeclRefAvailability(ConcreteDeclRef declRef, SourceRange R,
2821-
const ApplyExpr *call = nullptr,
2858+
const Expr *call = nullptr,
28222859
DeclAvailabilityFlags flags = None) const;
28232860

28242861
private:
@@ -3001,9 +3038,8 @@ class ExprAvailabilityWalker : public ASTWalker {
30013038

30023039
/// Diagnose uses of unavailable declarations. Returns true if a diagnostic
30033040
/// was emitted.
3004-
bool
3005-
ExprAvailabilityWalker::diagnoseDeclRefAvailability(
3006-
ConcreteDeclRef declRef, SourceRange R, const ApplyExpr *call,
3041+
bool ExprAvailabilityWalker::diagnoseDeclRefAvailability(
3042+
ConcreteDeclRef declRef, SourceRange R, const Expr *call,
30073043
DeclAvailabilityFlags Flags) const {
30083044
if (!declRef)
30093045
return false;
@@ -3012,7 +3048,8 @@ ExprAvailabilityWalker::diagnoseDeclRefAvailability(
30123048
if (auto *attr = AvailableAttr::isUnavailable(D)) {
30133049
if (diagnoseIncDecRemoval(D, R, attr))
30143050
return true;
3015-
if (call && diagnoseMemoryLayoutMigration(D, R, attr, call))
3051+
if (isa_and_nonnull<ApplyExpr>(call) &&
3052+
diagnoseMemoryLayoutMigration(D, R, attr, cast<ApplyExpr>(call)))
30163053
return true;
30173054
}
30183055

@@ -3030,12 +3067,10 @@ ExprAvailabilityWalker::diagnoseDeclRefAvailability(
30303067

30313068
/// Diagnose uses of unavailable declarations. Returns true if a diagnostic
30323069
/// was emitted.
3033-
bool
3034-
swift::diagnoseDeclAvailability(const ValueDecl *D,
3035-
SourceRange R,
3036-
const ApplyExpr *call,
3037-
const ExportContext &Where,
3038-
DeclAvailabilityFlags Flags) {
3070+
bool swift::diagnoseDeclAvailability(const ValueDecl *D, SourceRange R,
3071+
const Expr *call,
3072+
const ExportContext &Where,
3073+
DeclAvailabilityFlags Flags) {
30393074
assert(!Where.isImplicit());
30403075

30413076
// Generic parameters are always available.
@@ -3095,7 +3130,6 @@ swift::diagnoseDeclAvailability(const ValueDecl *D,
30953130
return false;
30963131
}
30973132

3098-
30993133
/// Return true if the specified type looks like an integer of floating point
31003134
/// type.
31013135
static bool isIntegerOrFloatingPointType(Type ty, ModuleDecl *M) {

lib/Sema/TypeCheckAvailability.h

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -230,10 +230,8 @@ diagnoseSubstitutionMapAvailability(SourceLoc loc,
230230

231231
/// Diagnose uses of unavailable declarations. Returns true if a diagnostic
232232
/// was emitted.
233-
bool diagnoseDeclAvailability(const ValueDecl *D,
234-
SourceRange R,
235-
const ApplyExpr *call,
236-
const ExportContext &where,
233+
bool diagnoseDeclAvailability(const ValueDecl *D, SourceRange R,
234+
const Expr *call, const ExportContext &where,
237235
DeclAvailabilityFlags flags = None);
238236

239237
void diagnoseUnavailableOverride(ValueDecl *override,
@@ -242,10 +240,9 @@ void diagnoseUnavailableOverride(ValueDecl *override,
242240

243241
/// Emit a diagnostic for references to declarations that have been
244242
/// marked as unavailable, either through "unavailable" or "obsoleted:".
245-
bool diagnoseExplicitUnavailability(const ValueDecl *D,
246-
SourceRange R,
243+
bool diagnoseExplicitUnavailability(const ValueDecl *D, SourceRange R,
247244
const ExportContext &Where,
248-
const ApplyExpr *call,
245+
const Expr *call,
249246
DeclAvailabilityFlags Flags = None);
250247

251248
/// Emit a diagnostic for references to declarations that have been

lib/Sema/TypeChecker.h

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1049,10 +1049,8 @@ void diagnosePotentialAccessorUnavailability(
10491049
const AvailableAttr *getDeprecated(const Decl *D);
10501050

10511051
/// Emits a diagnostic for a reference to a declaration that is deprecated.
1052-
void diagnoseIfDeprecated(SourceRange SourceRange,
1053-
const ExportContext &Where,
1054-
const ValueDecl *DeprecatedDecl,
1055-
const ApplyExpr *Call);
1052+
void diagnoseIfDeprecated(SourceRange SourceRange, const ExportContext &Where,
1053+
const ValueDecl *DeprecatedDecl, const Expr *Call);
10561054

10571055
/// Emits a diagnostic for a reference to a conformnace that is deprecated.
10581056
bool diagnoseIfDeprecated(SourceLoc loc,

test/attr/attr_availability.swift

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1179,3 +1179,55 @@ func testMultipleTrailingClosures(_ x: TypeWithTrailingClosures) {
11791179
// expected-note@-1 {{use 'TypeWithTrailingClosures.variadicTrailingClosures(a:b:c:)' instead}} {{3-27=x.variadicTrailingClosures}} {{28-29=}} {{none}}
11801180
x.variadicTrailingClosures() {} _: {} _: {}
11811181
}
1182+
1183+
struct UnavailableSubscripts {
1184+
@available(*, unavailable, renamed: "subscript(new:)")
1185+
subscript(old index: Int) -> Int { 3 } // expected-note * {{'subscript(old:)' has been explicitly marked unavailable here}}
1186+
@available(*, unavailable, renamed: "subscript(new:)")
1187+
func getValue(old: Int) -> Int { 3 } // expected-note * {{'getValue(old:)' has been explicitly marked unavailable here}}
1188+
1189+
subscript(new index: Int) -> Int { 3 }
1190+
1191+
@available(*, unavailable, renamed: "getAValue(new:)")
1192+
subscript(getAValue index: Int) -> Int { 3 } // expected-note * {{'subscript(getAValue:)' has been explicitly marked unavailable here}}
1193+
func getAValue(new: Int) -> Int { 3 }
1194+
1195+
@available(*, unavailable, renamed: "subscript(arg1:arg2:arg3:)")
1196+
subscript(_ argg1: Int, _ argg2: Int, _ argg3: Int) -> Int { 3 } // expected-note * {{'subscript(_:_:_:)' has been explicitly marked unavailable here}}
1197+
1198+
@available(*, deprecated, renamed: "subscript(arg1:arg2:arg3:)")
1199+
subscript(argg1 argg1: Int, argg2 argg2: Int, argg3 argg3: Int) -> Int { 3 }
1200+
1201+
@available(*, deprecated, renamed: "subscript(arg1:arg2:arg3:)")
1202+
subscript(only1 only1: Int, only2 only2: Int) -> Int { 3 }
1203+
1204+
subscript(arg1 arg1: Int, arg2 arg2: Int, arg3 arg3: Int) -> Int { 3 }
1205+
1206+
@available(*, deprecated, renamed: "subscriptTo(_:)")
1207+
subscript(to to: Int) -> Int { 3 }
1208+
func subscriptTo(_ index: Int) -> Int { 3 }
1209+
1210+
func testUnavailableSubscripts(_ x: UnavailableSubscripts) {
1211+
_ = self[old: 3] // expected-error {{'subscript(old:)' has been renamed to 'subscript(new:)'}} {{14-17=new}}
1212+
_ = x[old: 3] // expected-error {{'subscript(old:)' has been renamed to 'subscript(new:)'}} {{11-14=new}}
1213+
1214+
_ = self.getValue(old: 3) // expected-error {{'getValue(old:)' has been renamed to 'subscript(new:)'}} {{14-22=[}} {{29-30=]}} {{23-26=new}}
1215+
_ = getValue(old: 3) // expected-error {{'getValue(old:)' has been renamed to 'subscript(new:)'}} {{9-9=self}} {{9-17=[}} {{24-25=]}} {{18-21=new}}
1216+
_ = x.getValue(old: 3) // expected-error {{'getValue(old:)' has been renamed to 'subscript(new:)'}} {{11-19=[}} {{26-27=]}} {{20-23=new}}
1217+
1218+
_ = self[getAValue: 3] // expected-error {{'subscript(getAValue:)' has been renamed to 'getAValue(new:)'}} {{13-14=.getAValue(}} {{26-27=)}} {{14-23=new}}
1219+
_ = x[getAValue: 3] // expected-error {{'subscript(getAValue:)' has been renamed to 'getAValue(new:)'}} {{10-11=.getAValue(}} {{23-24=)}} {{11-20=new}}
1220+
1221+
_ = self[argg1: 3, argg2: 3, argg3: 3] // expected-warning {{'subscript(argg1:argg2:argg3:)' is deprecated: renamed to 'subscript(arg1:arg2:arg3:)'}} // expected-note {{use 'subscript(arg1:arg2:arg3:)' instead}} {{14-19=arg1}} {{24-29=arg2}} {{34-39=arg3}}
1222+
_ = x[argg1: 3, argg2: 3, argg3: 3] // expected-warning {{'subscript(argg1:argg2:argg3:)' is deprecated: renamed to 'subscript(arg1:arg2:arg3:)'}} // expected-note {{use 'subscript(arg1:arg2:arg3:)' instead}} {{11-16=arg1}} {{21-26=arg2}} {{31-36=arg3}}
1223+
1224+
// Different number of parameters emit no fixit
1225+
_ = self[only1: 3, only2: 3] // expected-warning {{'subscript(only1:only2:)' is deprecated: renamed to 'subscript(arg1:arg2:arg3:)'}} // expected-note {{use 'subscript(arg1:arg2:arg3:)' instead}} {{none}}
1226+
1227+
_ = self[3, 3, 3] // expected-error {{'subscript(_:_:_:)' has been renamed to 'subscript(arg1:arg2:arg3:)'}} {{14-14=arg1: }} {{17-17=arg2: }} {{20-20=arg3: }}
1228+
_ = x[3, 3, 3] // expected-error {{'subscript(_:_:_:)' has been renamed to 'subscript(arg1:arg2:arg3:)'}} {{11-11=arg1: }} {{14-14=arg2: }} {{17-17=arg3: }}
1229+
1230+
_ = self[to: 3] // expected-warning {{'subscript(to:)' is deprecated: renamed to 'subscriptTo(_:)'}} // expected-note {{use 'subscriptTo(_:)' instead}} {{13-14=.subscriptTo(}} {{19-20=)}} {{14-18=}}
1231+
_ = x[to: 3] // expected-warning {{'subscript(to:)' is deprecated: renamed to 'subscriptTo(_:)'}} // expected-note {{use 'subscriptTo(_:)' instead}} {{10-11=.subscriptTo(}} {{16-17=)}} {{11-15=}}
1232+
}
1233+
}

0 commit comments

Comments
 (0)