Skip to content

Commit 51176d4

Browse files
committed
Fix diagnostics to produce valid rename fixit for SubscriptExpr
1 parent 9d4ce58 commit 51176d4

File tree

6 files changed

+122
-50
lines changed

6 files changed

+122
-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.startswith("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: 65 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1857,7 +1857,7 @@ static void fixItAvailableAttrRename(InFlightDiagnostic &diag,
18571857
SourceRange referenceRange,
18581858
const ValueDecl *renamedDecl,
18591859
const AvailableAttr *attr,
1860-
const ApplyExpr *call) {
1860+
const Expr *call) {
18611861
if (isa<AccessorDecl>(renamedDecl))
18621862
return;
18631863

@@ -1877,21 +1877,21 @@ static void fixItAvailableAttrRename(InFlightDiagnostic &diag,
18771877

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

1894-
auto *argExpr = call->getArg();
1894+
auto *argExpr = CE->getArg();
18951895
auto argList = getOriginalArgumentList(argExpr);
18961896

18971897
size_t numElementsWithinParens = argList.args.size();
@@ -1988,54 +1988,83 @@ static void fixItAvailableAttrRename(InFlightDiagnostic &diag,
19881988
selfReplace += base;
19891989
if (needsParens)
19901990
selfReplace.push_back(')');
1991+
19911992
selfReplace.push_back('.');
19921993
selfReplace += parsed.BaseName;
1993-
diag.fixItReplace(call->getFn()->getSourceRange(), selfReplace);
1994+
1995+
diag.fixItReplace(CE->getFn()->getSourceRange(), selfReplace);
19941996

19951997
if (!parsed.isPropertyAccessor())
19961998
diag.fixItRemoveChars(removeRangeStart, removeRangeEnd);
19971999

19982000
// Continue on to diagnose any argument label renames.
19992001

20002002
} else if (parsed.BaseName == "init" && isa_and_nonnull<CallExpr>(call)) {
2003+
auto *CE = dyn_cast<CallExpr>(call);
2004+
20012005
// For initializers, replace with a "call" of the context type...but only
20022006
// if we know we're doing a call (rather than a first-class reference).
20032007
if (parsed.isMember()) {
2004-
diag.fixItReplace(call->getFn()->getSourceRange(), parsed.ContextName);
2005-
2006-
} else if (auto *dotCall = dyn_cast<DotSyntaxCallExpr>(call->getFn())) {
2008+
diag.fixItReplace(CE->getFn()->getSourceRange(), parsed.ContextName);
2009+
} else if (auto *dotCall = dyn_cast<DotSyntaxCallExpr>(CE->getFn())) {
20072010
SourceLoc removeLoc = dotCall->getDotLoc();
20082011
if (removeLoc.isInvalid())
20092012
return;
20102013

20112014
diag.fixItRemove(SourceRange(removeLoc, dotCall->getFn()->getEndLoc()));
2012-
} else if (!isa<ConstructorRefCallExpr>(call->getFn())) {
2015+
} else if (!isa<ConstructorRefCallExpr>(CE->getFn())) {
20132016
return;
20142017
}
20152018

20162019
// Continue on to diagnose any constructor argument label renames.
2017-
2020+
2021+
} else if (parsed.IsSubscript) {
2022+
if (auto *CE = dyn_cast_or_null<CallExpr>(call)) {
2023+
// Renaming from CallExpr to SubscriptExpr. Remove function name and
2024+
// replace parens with square brackets.
2025+
diag.fixItReplace(CE->getFn()->getEndLoc(), "[");
2026+
diag.fixItReplace(CE->getEndLoc(), "]");
2027+
}
20182028
} else {
20192029
// Just replace the base name.
20202030
SmallString<64> baseReplace;
2031+
20212032
if (!parsed.ContextName.empty()) {
20222033
baseReplace += parsed.ContextName;
20232034
baseReplace += '.';
20242035
}
20252036
baseReplace += parsed.BaseName;
2026-
if (parsed.IsFunctionName && parsed.ArgumentLabels.empty() &&
2027-
isa<VarDecl>(renamedDecl)) {
2028-
// If we're going from a var to a function with no arguments, emit an
2029-
// empty parameter list.
2030-
baseReplace += "()";
2037+
2038+
if (parsed.IsFunctionName && isa_and_nonnull<SubscriptExpr>(call)) {
2039+
auto *SE = dyn_cast<SubscriptExpr>(call);
2040+
// Renaming from SubscriptExpr to CallExpr. Insert function name and
2041+
// replace square brackets with parens.
2042+
2043+
diag.fixItReplace(SE->getIndex()->getStartLoc(),
2044+
("." + baseReplace.str()).str());
2045+
diag.fixItReplace(SE->getEndLoc(), ")");
2046+
} else {
2047+
if (parsed.IsFunctionName && parsed.ArgumentLabels.empty() &&
2048+
isa<VarDecl>(renamedDecl)) {
2049+
// If we're going from a var to a function with no arguments, emit an
2050+
// empty parameter list.
2051+
baseReplace += "()";
2052+
}
2053+
diag.fixItReplace(referenceRange, baseReplace);
20312054
}
2032-
diag.fixItReplace(referenceRange, baseReplace);
20332055
}
20342056

2035-
if (!call || !isa<CallExpr>(call))
2057+
if (!call)
2058+
return;
2059+
2060+
Expr *argExpr;
2061+
if (auto *CE = dyn_cast<CallExpr>(call))
2062+
argExpr = CE->getArg();
2063+
else if (auto *SE = dyn_cast<SubscriptExpr>(call))
2064+
argExpr = SE->getIndex();
2065+
else
20362066
return;
20372067

2038-
auto *argExpr = call->getArg();
20392068
auto argList = getOriginalArgumentList(argExpr);
20402069

20412070
if (parsed.IsGetter) {
@@ -2148,7 +2177,8 @@ static void fixItAvailableAttrRename(InFlightDiagnostic &diag,
21482177
if (argList.isUnlabeledTrailingClosureIdx(i))
21492178
continue;
21502179
if (argumentLabelIDs[i] != argList.labels[i]) {
2151-
diagnoseArgumentLabelError(ctx, argExpr, argumentLabelIDs, false, &diag);
2180+
diagnoseArgumentLabelError(ctx, argExpr, argumentLabelIDs,
2181+
parsed.IsSubscript, &diag);
21522182
return;
21532183
}
21542184
}
@@ -2191,7 +2221,7 @@ describeRename(ASTContext &ctx, const AvailableAttr *attr, const ValueDecl *D,
21912221
name << parsed.ContextName << '.';
21922222

21932223
if (parsed.IsFunctionName) {
2194-
name << parsed.formDeclName(ctx);
2224+
name << parsed.formDeclName(ctx, (D && isa<SubscriptDecl>(D)));
21952225
} else {
21962226
name << parsed.BaseName;
21972227
}
@@ -2208,7 +2238,7 @@ describeRename(ASTContext &ctx, const AvailableAttr *attr, const ValueDecl *D,
22082238
void TypeChecker::diagnoseIfDeprecated(SourceRange ReferenceRange,
22092239
const ExportContext &Where,
22102240
const ValueDecl *DeprecatedDecl,
2211-
const ApplyExpr *Call) {
2241+
const Expr *Call) {
22122242
const AvailableAttr *Attr = TypeChecker::getDeprecated(DeprecatedDecl);
22132243
if (!Attr)
22142244
return;
@@ -2392,10 +2422,9 @@ void swift::diagnoseUnavailableOverride(ValueDecl *override,
23922422

23932423
/// Emit a diagnostic for references to declarations that have been
23942424
/// marked as unavailable, either through "unavailable" or "obsoleted:".
2395-
bool swift::diagnoseExplicitUnavailability(const ValueDecl *D,
2396-
SourceRange R,
2425+
bool swift::diagnoseExplicitUnavailability(const ValueDecl *D, SourceRange R,
23972426
const ExportContext &Where,
2398-
const ApplyExpr *call,
2427+
const Expr *call,
23992428
DeclAvailabilityFlags Flags) {
24002429
return diagnoseExplicitUnavailability(D, R, Where, Flags,
24012430
[=](InFlightDiagnostic &diag) {
@@ -2762,7 +2791,7 @@ class ExprAvailabilityWalker : public ASTWalker {
27622791
diagnoseDeclRefAvailability(DS->getMember(), DS->getSourceRange());
27632792
if (auto S = dyn_cast<SubscriptExpr>(E)) {
27642793
if (S->hasDecl()) {
2765-
diagnoseDeclRefAvailability(S->getDecl(), S->getSourceRange());
2794+
diagnoseDeclRefAvailability(S->getDecl(), S->getSourceRange(), S);
27662795
maybeDiagStorageAccess(S->getDecl().getDecl(), S->getSourceRange(), DC);
27672796
}
27682797
}
@@ -2820,7 +2849,7 @@ class ExprAvailabilityWalker : public ASTWalker {
28202849
}
28212850

28222851
bool diagnoseDeclRefAvailability(ConcreteDeclRef declRef, SourceRange R,
2823-
const ApplyExpr *call = nullptr,
2852+
const Expr *call = nullptr,
28242853
DeclAvailabilityFlags flags = None) const;
28252854

28262855
private:
@@ -3003,9 +3032,8 @@ class ExprAvailabilityWalker : public ASTWalker {
30033032

30043033
/// Diagnose uses of unavailable declarations. Returns true if a diagnostic
30053034
/// was emitted.
3006-
bool
3007-
ExprAvailabilityWalker::diagnoseDeclRefAvailability(
3008-
ConcreteDeclRef declRef, SourceRange R, const ApplyExpr *call,
3035+
bool ExprAvailabilityWalker::diagnoseDeclRefAvailability(
3036+
ConcreteDeclRef declRef, SourceRange R, const Expr *call,
30093037
DeclAvailabilityFlags Flags) const {
30103038
if (!declRef)
30113039
return false;
@@ -3014,7 +3042,8 @@ ExprAvailabilityWalker::diagnoseDeclRefAvailability(
30143042
if (auto *attr = AvailableAttr::isUnavailable(D)) {
30153043
if (diagnoseIncDecRemoval(D, R, attr))
30163044
return true;
3017-
if (call && diagnoseMemoryLayoutMigration(D, R, attr, call))
3045+
if (isa_and_nonnull<ApplyExpr>(call) &&
3046+
diagnoseMemoryLayoutMigration(D, R, attr, dyn_cast<ApplyExpr>(call)))
30183047
return true;
30193048
}
30203049

@@ -3032,12 +3061,10 @@ ExprAvailabilityWalker::diagnoseDeclRefAvailability(
30323061

30333062
/// Diagnose uses of unavailable declarations. Returns true if a diagnostic
30343063
/// was emitted.
3035-
bool
3036-
swift::diagnoseDeclAvailability(const ValueDecl *D,
3037-
SourceRange R,
3038-
const ApplyExpr *call,
3039-
const ExportContext &Where,
3040-
DeclAvailabilityFlags Flags) {
3064+
bool swift::diagnoseDeclAvailability(const ValueDecl *D, SourceRange R,
3065+
const Expr *call,
3066+
const ExportContext &Where,
3067+
DeclAvailabilityFlags Flags) {
30413068
assert(!Where.isImplicit());
30423069

30433070
// Generic parameters are always available.
@@ -3097,7 +3124,6 @@ swift::diagnoseDeclAvailability(const ValueDecl *D,
30973124
return false;
30983125
}
30993126

3100-
31013127
/// Return true if the specified type looks like an integer of floating point
31023128
/// type.
31033129
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
@@ -1042,10 +1042,8 @@ void diagnosePotentialAccessorUnavailability(
10421042
const AvailableAttr *getDeprecated(const Decl *D);
10431043

10441044
/// Emits a diagnostic for a reference to a declaration that is deprecated.
1045-
void diagnoseIfDeprecated(SourceRange SourceRange,
1046-
const ExportContext &Where,
1047-
const ValueDecl *DeprecatedDecl,
1048-
const ApplyExpr *Call);
1045+
void diagnoseIfDeprecated(SourceRange SourceRange, const ExportContext &Where,
1046+
const ValueDecl *DeprecatedDecl, const Expr *Call);
10491047

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

test/attr/attr_availability.swift

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1179,3 +1179,47 @@ 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+
func testUnavailableSubscripts(_ x: UnavailableSubscripts) {
1207+
_ = self[old: 3] // expected-error {{'subscript(old:)' has been renamed to 'subscript(new:)'}} {{14-17=new}}
1208+
_ = x[old: 3] // expected-error {{'subscript(old:)' has been renamed to 'subscript(new:)'}} {{11-14=new}}
1209+
1210+
_ = self.getValue(old: 3) // expected-error {{'getValue(old:)' has been renamed to 'subscript(new:)'}} {{14-22=[}} {{29-30=]}} {{23-26=new}}
1211+
_ = x.getValue(old: 3) // expected-error {{'getValue(old:)' has been renamed to 'subscript(new:)'}} {{11-19=[}} {{26-27=]}} {{20-23=new}}
1212+
1213+
_ = self[getAValue: 3] // expected-error {{'subscript(getAValue:)' has been renamed to 'getAValue(new:)'}} {{13-14=.getAValue}} {{26-27=)}} {{14-23=new}}
1214+
_ = x[getAValue: 3] // expected-error {{'subscript(getAValue:)' has been renamed to 'getAValue(new:)'}} {{10-11=.getAValue}} {{23-24=)}} {{11-20=new}}
1215+
1216+
_ = 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}}
1217+
_ = 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}}
1218+
1219+
// Different number of parameters emit no fixit
1220+
_ = 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}}
1221+
1222+
_ = self[3, 3, 3] // expected-error {{'subscript(_:_:_:)' has been renamed to 'subscript(arg1:arg2:arg3:)'}} {{14-14=arg1: }} {{17-17=arg2: }} {{20-20=arg3: }}
1223+
_ = x[3, 3, 3] // expected-error {{'subscript(_:_:_:)' has been renamed to 'subscript(arg1:arg2:arg3:)'}} {{11-11=arg1: }} {{14-14=arg2: }} {{17-17=arg3: }}
1224+
}
1225+
}

0 commit comments

Comments
 (0)