Skip to content

Commit 894318c

Browse files
committed
Allow instance member syntax in @available(renamed:).
Example: @available(*, unavailable, renamed: "Sequence.enumerated(self:)") func enumerate<Seq: SequenceType>(_ sequence: Seq) -> EnumerateSequence<Seq> This will allow us to reuse this logic to suggest fixes for APIs turned into members by NS_SWIFT_NAME.
1 parent 7e06ee4 commit 894318c

File tree

4 files changed

+130
-10
lines changed

4 files changed

+130
-10
lines changed

lib/Frontend/DiagnosticVerifier.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -697,7 +697,7 @@ bool swift::verifyDiagnostics(SourceManager &SM, ArrayRef<unsigned> BufferIDs) {
697697
auto *Verifier = (DiagnosticVerifier*)SM.getLLVMSourceMgr().getDiagContext();
698698
SM.getLLVMSourceMgr().setDiagHandler(nullptr, nullptr);
699699

700-
bool autoApplyFixes = false;
700+
bool autoApplyFixes = true;
701701

702702
bool HadError = false;
703703

lib/Parse/ParseDecl.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -886,8 +886,7 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
886886
Message = Value.getValue();
887887
} else {
888888
ParsedDeclName parsedName = parseDeclName(Value.getValue());
889-
if (!parsedName || parsedName.isInstanceMember() ||
890-
parsedName.isPropertyAccessor()) {
889+
if (!parsedName || parsedName.isPropertyAccessor()) {
891890
diagnose(Loc, diag::attr_availability_invalid_renamed, AttrName);
892891
DiscardAttribute = true;
893892
continue;

lib/Sema/MiscDiagnostics.cpp

Lines changed: 88 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1008,16 +1008,97 @@ void swift::fixItAvailableAttrRename(TypeChecker &TC,
10081008
const AvailableAttr *attr,
10091009
const CallExpr *CE) {
10101010
ParsedDeclName parsed = swift::parseDeclName(attr->Rename);
1011-
if (!parsed || parsed.isInstanceMember() || parsed.isPropertyAccessor())
1011+
if (!parsed || parsed.isPropertyAccessor())
10121012
return;
10131013

1014-
SmallString<64> baseReplace;
1015-
if (!parsed.ContextName.empty()) {
1016-
baseReplace += parsed.ContextName;
1017-
baseReplace += '.';
1014+
if (parsed.isInstanceMember()) {
1015+
// Replace the base of the call with the "self argument".
1016+
// We can only do a good job with the fix-it if we have the whole call
1017+
// expression.
1018+
// FIXME: Should we be validating the ContextName in some way?
1019+
if (!CE)
1020+
return;
1021+
assert(parsed.IsFunctionName && "instance members use function syntax");
1022+
1023+
SourceManager &sourceMgr = TC.Context.SourceMgr;
1024+
1025+
unsigned selfIndex = parsed.SelfIndex.getValue();
1026+
const Expr *selfExpr = nullptr;
1027+
SourceRange selfArgRange;
1028+
SourceLoc endOfPreviousArg;
1029+
1030+
const Expr *argExpr = CE->getArg();
1031+
if (auto args = dyn_cast<TupleExpr>(argExpr)) {
1032+
if (selfIndex >= args->getNumElements() ||
1033+
parsed.ArgumentLabels.size() != args->getNumElements() - 1) {
1034+
return;
1035+
}
1036+
1037+
selfExpr = args->getElement(selfIndex);
1038+
selfArgRange = selfExpr->getSourceRange();
1039+
1040+
SourceLoc labelLoc = args->getElementNameLoc(selfIndex);
1041+
if (labelLoc.isValid())
1042+
selfArgRange.Start = labelLoc;
1043+
1044+
if (selfIndex > 0) {
1045+
endOfPreviousArg = args->getElement(selfIndex-1)->getEndLoc();
1046+
endOfPreviousArg = Lexer::getLocForEndOfToken(sourceMgr,
1047+
endOfPreviousArg);
1048+
}
1049+
1050+
// Avoid later argument label fix-its for this argument.
1051+
Identifier oldLabel = args->getElementName(selfIndex);
1052+
StringRef oldLabelStr;
1053+
if (!oldLabel.empty())
1054+
oldLabelStr = oldLabel.str();
1055+
parsed.ArgumentLabels.insert(parsed.ArgumentLabels.begin() + selfIndex,
1056+
oldLabelStr);
1057+
1058+
} else {
1059+
if (selfIndex != 0 || !parsed.ArgumentLabels.empty())
1060+
return;
1061+
selfExpr = cast<ParenExpr>(argExpr)->getSubExpr();
1062+
selfArgRange = argExpr->getSourceRange();
1063+
}
1064+
1065+
if (selfArgRange.Start.isInvalid() || selfArgRange.End.isInvalid())
1066+
return;
1067+
1068+
if (endOfPreviousArg.isInvalid())
1069+
endOfPreviousArg = selfArgRange.Start;
1070+
1071+
CharSourceRange selfExprRange =
1072+
Lexer::getCharSourceRangeFromSourceRange(sourceMgr,
1073+
selfExpr->getSourceRange());
1074+
bool needsParens = !selfExpr->canAppendCallParentheses();
1075+
1076+
SmallString<64> selfReplace;
1077+
if (needsParens)
1078+
selfReplace.push_back('(');
1079+
selfReplace += sourceMgr.extractText(selfExprRange);
1080+
if (needsParens)
1081+
selfReplace.push_back(')');
1082+
selfReplace.push_back('.');
1083+
selfReplace += parsed.BaseName;
1084+
diag.fixItReplace(CE->getFn()->getSourceRange(), selfReplace);
1085+
1086+
diag.fixItRemoveChars(endOfPreviousArg,
1087+
Lexer::getLocForEndOfToken(sourceMgr,
1088+
selfArgRange.End));
1089+
1090+
// Continue on to diagnose any argument label renames.
1091+
1092+
} else {
1093+
// Just replace the base name.
1094+
SmallString<64> baseReplace;
1095+
if (!parsed.ContextName.empty()) {
1096+
baseReplace += parsed.ContextName;
1097+
baseReplace += '.';
1098+
}
1099+
baseReplace += parsed.BaseName;
1100+
diag.fixItReplace(referenceRange, baseReplace);
10181101
}
1019-
baseReplace += parsed.BaseName;
1020-
diag.fixItReplace(referenceRange, baseReplace);
10211102

10221103
if (!CE || !parsed.IsFunctionName)
10231104
return;

test/attr/attr_availability.swift

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,3 +337,43 @@ func testRenameArgMismatch() {
337337
unavailableTooManyUnnamed(0) // expected-error{{'unavailableTooManyUnnamed' has been renamed to 'shinyLabeledArguments(a:b:)'}} {{3-28=shinyLabeledArguments}}
338338
unavailableNoArgsTooMany() // expected-error{{'unavailableNoArgsTooMany()' has been renamed to 'shinyLabeledArguments(a:)'}} {{3-27=shinyLabeledArguments}}
339339
}
340+
341+
@available(*, unavailable, renamed: "Int.foo(self:)")
342+
func unavailableInstance(a: Int) {} // expected-note 2 {{here}}
343+
@available(*, unavailable, renamed: "Int.foo(self:)")
344+
func unavailableInstanceUnlabeled(_ a: Int) {} // expected-note {{here}}
345+
@available(*, unavailable, renamed: "Int.foo(self:other:)")
346+
func unavailableInstanceFirst(a: Int, b: Int) {} // expected-note {{here}}
347+
@available(*, unavailable, renamed: "Int.foo(other:self:)")
348+
func unavailableInstanceSecond(a: Int, b: Int) {} // expected-note {{here}}
349+
@available(*, unavailable, renamed: "Int.foo(_:self:c:)")
350+
func unavailableInstanceSecondOfThree(a: Int, b: Int, c: Int) {} // expected-note {{here}}
351+
352+
func testRenameInstance() {
353+
unavailableInstance(a: 0) // expected-error{{'unavailableInstance(a:)' has been renamed to 'Int.foo(self:)'}} {{3-22=0.foo}} {{23-27=}}
354+
unavailableInstanceUnlabeled(0) // expected-error{{'unavailableInstanceUnlabeled' has been renamed to 'Int.foo(self:)'}} {{3-31=0.foo}} {{31-34=}}
355+
unavailableInstanceFirst(a: 0, b: 1) // expected-error{{'unavailableInstanceFirst(a:b:)' has been renamed to 'Int.foo(self:other:)'}} {{3-27=0.foo}} {{28-32=}} {{34-35=other}}
356+
unavailableInstanceSecond(a: 0, b: 1) // expected-error{{'unavailableInstanceSecond(a:b:)' has been renamed to 'Int.foo(other:self:)'}} {{3-28=1.foo}} {{29-30=other}} {{33-39=}}
357+
unavailableInstanceSecondOfThree(a: 0, b: 1, c: 2) // expected-error{{'unavailableInstanceSecondOfThree(a:b:c:)' has been renamed to 'Int.foo(_:self:c:)'}} {{3-35=1.foo}} {{36-39=}} {{40-46=}}
358+
359+
unavailableInstance(a: 0 + 0) // expected-error{{'unavailableInstance(a:)' has been renamed to 'Int.foo(self:)'}} {{3-22=(0 + 0).foo}} {{23-31=}}
360+
}
361+
362+
@available(*, unavailable, renamed: "Int.shinyLabeledArguments(self:)")
363+
func unavailableInstanceTooFew(a: Int, b: Int) {} // expected-note {{here}}
364+
@available(*, unavailable, renamed: "Int.shinyLabeledArguments(self:)")
365+
func unavailableInstanceTooFewUnnamed(_ a: Int, _ b: Int) {} // expected-note {{here}}
366+
@available(*, unavailable, renamed: "Int.shinyLabeledArguments(self:b:)")
367+
func unavailableInstanceTooMany(a: Int) {} // expected-note {{here}}
368+
@available(*, unavailable, renamed: "Int.shinyLabeledArguments(self:b:)")
369+
func unavailableInstanceTooManyUnnamed(_ a: Int) {} // expected-note {{here}}
370+
@available(*, unavailable, renamed: "Int.shinyLabeledArguments(self:)")
371+
func unavailableInstanceNoArgsTooMany() {} // expected-note {{here}}
372+
373+
func testRenameInstanceArgMismatch() {
374+
unavailableInstanceTooFew(a: 0, b: 1) // expected-error{{'unavailableInstanceTooFew(a:b:)' has been renamed to 'Int.shinyLabeledArguments(self:)'}} {{none}}
375+
unavailableInstanceTooFewUnnamed(0, 1) // expected-error{{'unavailableInstanceTooFewUnnamed' has been renamed to 'Int.shinyLabeledArguments(self:)'}} {{none}}
376+
unavailableInstanceTooMany(a: 0) // expected-error{{'unavailableInstanceTooMany(a:)' has been renamed to 'Int.shinyLabeledArguments(self:b:)'}} {{none}}
377+
unavailableInstanceTooManyUnnamed(0) // expected-error{{'unavailableInstanceTooManyUnnamed' has been renamed to 'Int.shinyLabeledArguments(self:b:)'}} {{none}}
378+
unavailableInstanceNoArgsTooMany() // expected-error{{'unavailableInstanceNoArgsTooMany()' has been renamed to 'Int.shinyLabeledArguments(self:)'}} {{none}}
379+
}

0 commit comments

Comments
 (0)