blob: 38535f82610fcc8a8de989361405c4ad613098eb [file] [log] [blame]
//===--- APIDiffMigratorPass.cpp ------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
#include "swift/AST/USRGeneration.h"
#include "swift/AST/ASTVisitor.h"
#include "swift/Basic/StringExtras.h"
#include "swift/Frontend/Frontend.h"
#include "swift/IDE/Utils.h"
#include "swift/Sema/IDETypeChecking.h"
#include "swift/Migrator/ASTMigratorPass.h"
#include "swift/Migrator/EditorAdapter.h"
#include "swift/Migrator/FixitApplyDiagnosticConsumer.h"
#include "swift/Migrator/Migrator.h"
#include "swift/Migrator/RewriteBufferEditsReceiver.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/FileManager.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Edit/EditedSource.h"
#include "clang/Rewrite/Core/RewriteBuffer.h"
#include "llvm/Support/FileSystem.h"
#include "swift/IDE/APIDigesterData.h"
#include "swift/Basic/Defer.h"
using namespace swift;
using namespace swift::migrator;
using namespace swift::ide;
using namespace swift::ide::api;
namespace {
struct FoundResult {
SourceRange TokenRange;
bool Optional; // Range includes a trailing ? or !, e.g. [SomeType!]
bool Suffixable; // No need to wrap parens when adding optionality
bool Suffixed; // Range is followed by a trailing ? or !, e.g. [SomeType]!
bool isValid() const { return TokenRange.isValid(); }
};
class ChildIndexFinder : public TypeReprVisitor<ChildIndexFinder, FoundResult> {
ArrayRef<uint8_t> ChildIndices;
bool ParentIsOptional;
bool IsFunctionTypeArgument;
public:
ChildIndexFinder(ArrayRef<uint8_t> ChildIndices)
: ChildIndices(ChildIndices), ParentIsOptional(false),
IsFunctionTypeArgument(false) {}
FoundResult findChild(AbstractFunctionDecl *Parent) {
auto NextIndex = consumeNext();
if (!NextIndex) {
if (auto Func = dyn_cast<FuncDecl>(Parent))
return findChild(Func->getBodyResultTypeLoc());
if (auto Init = dyn_cast<ConstructorDecl>(Parent)) {
SourceLoc End = Init->getFailabilityLoc();
bool Optional = End.isValid();
if (!Optional)
End = Init->getNameLoc();
return {SourceRange(Init->getNameLoc(), End), Optional,
/*suffixable=*/true, /*suffixed=*/false};
}
return {SourceRange(), false, false, false};
}
for (auto *Param: *Parent->getParameters()) {
if (!--NextIndex) {
return findChild(Param->getTypeRepr());
}
}
llvm_unreachable("child index out of bounds");
}
private:
bool hasNextIndex() const {
return !ChildIndices.empty();
}
unsigned consumeNext() {
unsigned Next = ChildIndices.front();
ChildIndices = ChildIndices.drop_front();
return Next;
}
bool isUserTypeAlias(TypeRepr *T) const {
if (auto Ident = dyn_cast<ComponentIdentTypeRepr>(T)) {
if (auto Bound = Ident->getBoundDecl()) {
return isa<TypeAliasDecl>(Bound) &&
!Bound->getModuleContext()->isSystemModule();
}
}
return false;
}
FoundResult findChild(TypeLoc Loc) {
if (!Loc.hasLocation())
return {SourceRange(), false, false, false};
return visit(Loc.getTypeRepr());
}
public:
template<typename T>
FoundResult handleParent(TypeRepr *Parent, const ArrayRef<T> Children,
bool Optional = false, bool Suffixable = true) {
if (!hasNextIndex())
return {
Parent->getSourceRange(),
Optional, Suffixable, /*Suffixed=*/ParentIsOptional
};
auto NextIndex = consumeNext();
if (isUserTypeAlias(Parent))
return {SourceRange(), false, false, false};
assert(NextIndex < Children.size());
TypeRepr *Child = Children[NextIndex];
ParentIsOptional = Optional;
IsFunctionTypeArgument = NextIndex == 1 && isa<FunctionTypeRepr>(Parent);
return visit(Child);
}
FoundResult handleParent(TypeRepr *Parent, TypeRepr *FirstChild,
TypeRepr *SecondChild, bool Optional = false,
bool Suffixable = true) {
TypeRepr *Children[] = {FirstChild, SecondChild};
return handleParent(Parent, llvm::makeArrayRef(Children), Optional,
Suffixable);
}
FoundResult handleParent(TypeRepr *Parent, TypeRepr *Base,
bool Optional = false, bool Suffixable = true) {
return handleParent(Parent, llvm::makeArrayRef(Base), Optional, Suffixable);
}
FoundResult visitTypeRepr(TypeRepr *T) {
llvm_unreachable("unexpected typerepr");
}
FoundResult visitErrorTypeRepr(ErrorTypeRepr *T) {
return {SourceRange(), false, false, false};
}
FoundResult visitAttributedTypeRepr(AttributedTypeRepr *T) {
return visit(T->getTypeRepr());
}
FoundResult visitInOutTypeRepr(InOutTypeRepr *T) {
return visit(T->getBase());
}
FoundResult visitSharedTypeRepr(SharedTypeRepr *T) {
return visit(T->getBase());
}
FoundResult visitOwnedTypeRepr(OwnedTypeRepr *T) {
return visit(T->getBase());
}
FoundResult visitArrayTypeRepr(ArrayTypeRepr *T) {
return handleParent(T, T->getBase());
}
FoundResult visitDictionaryTypeRepr(DictionaryTypeRepr *T) {
return handleParent(T, T->getKey(), T->getValue());
}
FoundResult visitTupleTypeRepr(TupleTypeRepr *T) {
// Paren TupleTypeReprs may be arbitrarily nested so don't count as their
// own index level except in the case of function type argument parens
if (T->isParenType() && !IsFunctionTypeArgument) {
ParentIsOptional = false;
return visit(T->getElementType(0));
}
IsFunctionTypeArgument = false;
llvm::SmallVector<TypeRepr *, 8> Children;
T->getElementTypes(Children);
return handleParent(T, ArrayRef<TypeRepr *>(Children));
}
FoundResult visitFunctionTypeRepr(FunctionTypeRepr *T) {
return handleParent(T, T->getResultTypeRepr(), T->getArgsTypeRepr(),
/*Optional=*/false, /*Suffixable=*/false);
}
FoundResult visitCompositionTypeRepr(CompositionTypeRepr *T) {
return handleParent(T, T->getTypes(), /*Optional=*/false,
/*Suffixable=*/false);
}
FoundResult visitSimpleIdentTypeRepr(SimpleIdentTypeRepr *T) {
return handleParent(T, ArrayRef<TypeRepr*>());
}
FoundResult visitGenericIdentTypeRepr(GenericIdentTypeRepr *T) {
return handleParent(T, T->getGenericArgs());
}
FoundResult visitCompoundIdentTypeRepr(CompoundIdentTypeRepr *T) {
return visit(T->getComponents().back());
}
FoundResult visitOptionalTypeRepr(OptionalTypeRepr *T) {
return handleParent(T, T->getBase(), /*Optional=*/true);
}
FoundResult visitImplicitlyUnwrappedOptionalTypeRepr(ImplicitlyUnwrappedOptionalTypeRepr *T) {
return handleParent(T, T->getBase(), /*Optional=*/true);
}
FoundResult visitProtocolTypeRepr(ProtocolTypeRepr *T) {
return handleParent(T, T->getBase());
}
FoundResult visitMetatypeTypeRepr(MetatypeTypeRepr *T) {
return handleParent(T, T->getBase());
}
FoundResult visitFixedTypeRepr(FixedTypeRepr *T) {
return handleParent(T, ArrayRef<TypeRepr*>());
}
};
static ValueDecl* getReferencedDecl(Expr *E) {
// Get the syntactic expression out of an implicit expression.
if (auto *ICE = dyn_cast<ImplicitConversionExpr>(E))
E = ICE->getSyntacticSubExpr();
if (auto *DRE = dyn_cast<DeclRefExpr>(E)) {
return DRE->getDecl();
} else if (auto *MRE = dyn_cast<MemberRefExpr>(E)) {
return MRE->getMember().getDecl();
} else if (auto OtherCtorE = dyn_cast<OtherConstructorDeclRefExpr>(E)) {
return OtherCtorE->getDecl();
} else {
return nullptr;
}
}
struct ConversionFunctionInfo {
Expr *ExpressionToWrap;
SmallString<256> Buffer;
unsigned FuncNameStart;
unsigned FuncNameEnd;
ConversionFunctionInfo(Expr *ExpressionToWrap):
ExpressionToWrap(ExpressionToWrap) {}
StringRef getFuncDef() const { return Buffer.str(); }
StringRef getFuncName() const {
return Buffer.substr(FuncNameStart, FuncNameEnd - FuncNameStart);
}
};
struct APIDiffMigratorPass : public ASTMigratorPass, public SourceEntityWalker {
APIDiffItemStore DiffStore;
bool isNilExpr(Expr *E) {
auto Range = E->getSourceRange();
return Range.isValid() && Lexer::getCharSourceRangeFromSourceRange(
SF->getASTContext().SourceMgr, Range).str() == "nil";
}
bool isDotMember(CharSourceRange Range) {
auto S = Range.str();
return S.startswith(".") && S.substr(1).find(".") == StringRef::npos;
}
bool isDotMember(SourceRange Range) {
return isDotMember(Lexer::getCharSourceRangeFromSourceRange(
SF->getASTContext().SourceMgr, Range));
}
bool isDotMember(Expr *E) {
auto Range = E->getSourceRange();
return Range.isValid() && isDotMember(Range);
}
std::vector<APIDiffItem*> getRelatedDiffItems(ValueDecl *VD) {
std::vector<APIDiffItem*> results;
if (!VD)
return results;
auto addDiffItems = [&](ValueDecl *VD) {
llvm::SmallString<64> Buffer;
llvm::raw_svector_ostream OS(Buffer);
if (swift::ide::printValueDeclUSR(VD, OS))
return;
auto Items = DiffStore.getDiffItems(Buffer.str());
results.insert(results.end(), Items.begin(), Items.end());
};
addDiffItems(VD);
for (auto *Overridden: collectAllOverriddenDecls(VD,
/*IncludeProtocolReqs=*/true,
/*Transitive=*/true)) {
addDiffItems(Overridden);
}
return results;
}
DeclNameViewer getFuncRename(ValueDecl *VD, llvm::SmallString<32> &Buffer,
bool &IgnoreBase) {
for (auto *Item: getRelatedDiffItems(VD)) {
if (auto *CI = dyn_cast<CommonDiffItem>(Item)) {
if (CI->isRename()) {
IgnoreBase = true;
switch(CI->NodeKind) {
case SDKNodeKind::DeclFunction:
IgnoreBase = false;
LLVM_FALLTHROUGH;
case SDKNodeKind::DeclConstructor:
return DeclNameViewer(CI->getNewName());
default:
return DeclNameViewer();
}
}
}
if (auto *MI = dyn_cast<TypeMemberDiffItem>(Item)) {
if (MI->Subkind == TypeMemberDiffItemSubKind::FuncRename) {
llvm::raw_svector_ostream OS(Buffer);
OS << MI->getNewTypeAndDot() << MI->newPrintedName;
return DeclNameViewer(OS.str());
}
}
}
return DeclNameViewer();
}
bool isSimpleReplacement(APIDiffItem *Item, bool isDotMember, std::string &Text) {
if (auto *MD = dyn_cast<TypeMemberDiffItem>(Item)) {
if (MD->Subkind == TypeMemberDiffItemSubKind::SimpleReplacement) {
bool NeedNoTypeName = isDotMember &&
MD->oldPrintedName == MD->newPrintedName;
if (NeedNoTypeName) {
Text = (llvm::Twine(MD->isNewNameGlobal() ? "" : ".") +
MD->getNewName().base()).str();
} else {
Text = (llvm::Twine(MD->getNewTypeAndDot()) +
MD->getNewName().base()).str();
}
return true;
}
}
// Simple rename.
if (auto CI = dyn_cast<CommonDiffItem>(Item)) {
if (CI->isRename() && (CI->NodeKind == SDKNodeKind::DeclVar ||
CI->NodeKind == SDKNodeKind::DeclType)) {
Text = CI->getNewName();
return true;
}
}
return false;
}
std::vector<ConversionFunctionInfo> HelperFuncInfo;
SourceLoc FileEndLoc;
llvm::StringSet<> OverridingRemoveNames;
/// For a given expression, check whether the type of this expression is
/// type alias type, and the type alias type is known to change to raw
/// representable type.
bool isRecognizedTypeAliasChange(Expr *E) {
if (auto Ty = E->getType()) {
if (auto *NT = dyn_cast<TypeAliasType>(Ty.getPointer())) {
for (auto Item: getRelatedDiffItems(NT->getDecl())) {
if (auto CI = dyn_cast<CommonDiffItem>(Item)) {
if (CI->DiffKind == NodeAnnotation::TypeAliasDeclToRawRepresentable) {
return true;
}
}
}
}
}
return false;
}
APIDiffMigratorPass(EditorAdapter &Editor, SourceFile *SF,
const MigratorOptions &Opts):
ASTMigratorPass(Editor, SF, Opts), DiffStore(Diags),
FileEndLoc(SM.getRangeForBuffer(BufferID).getEnd()),
OverridingRemoveNames(funcNamesForOverrideRemoval()) {}
~APIDiffMigratorPass() {
Editor.disableCache();
SWIFT_DEFER { Editor.enableCache(); };
// Collect inserted functions to avoid re-insertion.
std::set<std::string> InsertedFunctions;
SmallVector<Decl*, 16> TopDecls;
SF->getTopLevelDecls(TopDecls);
for (auto *D: TopDecls) {
if (auto *FD = dyn_cast<FuncDecl>(D)) {
InsertedFunctions.insert(FD->getBaseName().getIdentifier().str());
}
}
// Handle helper functions without wrappees first.
for (auto &Cur: HelperFuncInfo) {
if (Cur.ExpressionToWrap)
continue;
auto FuncName = Cur.getFuncName();
// Avoid inserting the helper function if it's already present.
if (!InsertedFunctions.count(FuncName)) {
Editor.insert(FileEndLoc, Cur.getFuncDef());
InsertedFunctions.insert(FuncName);
}
}
// Remove all helper functions that're without expressions to wrap.
HelperFuncInfo.erase(std::remove_if(HelperFuncInfo.begin(),
HelperFuncInfo.end(), [](const ConversionFunctionInfo& Info) {
return !Info.ExpressionToWrap;
}), HelperFuncInfo.end());
for (auto &Cur: HelperFuncInfo) {
assert(Cur.ExpressionToWrap);
// Avoid wrapping nil expression.
if (isNilExpr(Cur.ExpressionToWrap))
continue;
// Avoid wrapping a single expression with multiple conversion functions.
auto count = std::count_if(HelperFuncInfo.begin(), HelperFuncInfo.end(),
[&] (ConversionFunctionInfo &Info) {
return Info.ExpressionToWrap->getSourceRange() ==
Cur.ExpressionToWrap->getSourceRange();
});
if (count > 1)
continue;
assert(count == 1);
// A conversion function will be redundant if the expression will change
// from type alias to raw-value representable.
if (isRecognizedTypeAliasChange(Cur.ExpressionToWrap))
continue;
auto FuncName = Cur.getFuncName();
// Avoid inserting the helper function if it's already present.
if (!InsertedFunctions.count(FuncName)) {
Editor.insert(FileEndLoc, Cur.getFuncDef());
InsertedFunctions.insert(FuncName);
}
Editor.insertBefore(Cur.ExpressionToWrap->getStartLoc(),
(Twine(FuncName) + "(").str());
Editor.insertAfterToken(Cur.ExpressionToWrap->getEndLoc(), ")");
}
}
void run() {
if (Opts.APIDigesterDataStorePaths.empty())
return;
for (auto Path : Opts.APIDigesterDataStorePaths)
DiffStore.addStorePath(Path);
DiffStore.printIncomingUsr(Opts.DumpUsr);
walk(SF);
}
bool updateStringRepresentableDeclRef(APIDiffItem *Diff,
CharSourceRange Range) {
auto *CD = dyn_cast<CommonDiffItem>(Diff);
if (!CD)
return false;
if (CD->NodeKind != SDKNodeKind::DeclVar)
return false;
if (!CD->isStringRepresentableChange())
return false;
switch(CD->DiffKind) {
case NodeAnnotation::SimpleStringRepresentableUpdate:
Editor.insert(Range.getEnd(), ".rawValue");
return true;
default:
return false;
}
}
bool visitDeclReference(ValueDecl *D, CharSourceRange Range,
TypeDecl *CtorTyRef, ExtensionDecl *ExtTyRef,
Type T, ReferenceMetaData Data) override {
if (Data.isImplicit)
return true;
for (auto *Item: getRelatedDiffItems(CtorTyRef ? CtorTyRef: D)) {
std::string RepText;
if (isSimpleReplacement(Item, isDotMember(Range), RepText)) {
Editor.replace(Range, RepText);
return true;
}
}
return true;
}
struct ReferenceCollector : public SourceEntityWalker {
ValueDecl *Target;
CharSourceRange Result;
ReferenceCollector(ValueDecl* Target) : Target(Target) {}
bool visitDeclReference(ValueDecl *D, CharSourceRange Range,
TypeDecl *CtorTyRef, ExtensionDecl *ExtTyRef,
Type T, ReferenceMetaData Data) override {
if (D == Target && !Data.isImplicit) {
Result = Range;
return false;
}
return true;
}
};
void emitRenameLabelChanges(Expr *Arg, DeclNameViewer NewName,
llvm::ArrayRef<unsigned> IgnoreArgIndex) {
unsigned Idx = 0;
auto Ranges = getCallArgLabelRanges(SM, Arg,
LabelRangeEndAt::LabelNameOnly);
llvm::SmallVector<uint8_t, 2> ToRemoveIndices;
for (unsigned I = 0; I < Ranges.size(); I ++) {
if (std::any_of(IgnoreArgIndex.begin(), IgnoreArgIndex.end(),
[I](unsigned Ig) { return Ig == I; }))
continue;
auto LR = Ranges[I];
if (Idx < NewName.argSize()) {
auto Label = NewName.args()[Idx++];
if (!Label.empty()) {
if (LR.getByteLength())
Editor.replace(LR, Label);
else
Editor.insert(LR.getStart(), (llvm::Twine(Label) + ": ").str());
} else if (LR.getByteLength()){
// New label is "_" however the old label is explicit.
ToRemoveIndices.push_back(I);
}
}
}
if (!ToRemoveIndices.empty()) {
auto Ranges = getCallArgLabelRanges(SM, Arg,
LabelRangeEndAt::BeforeElemStart);
for (auto I : ToRemoveIndices) {
Editor.remove(Ranges[I]);
}
}
}
void handleFuncRename(ValueDecl *FD, Expr* FuncRefContainer, Expr *Arg) {
bool IgnoreBase = false;
llvm::SmallString<32> Buffer;
if (auto View = getFuncRename(FD, Buffer, IgnoreBase)) {
if (!IgnoreBase) {
ReferenceCollector Walker(FD);
Walker.walk(FuncRefContainer);
Editor.replace(Walker.Result, View.base());
}
emitRenameLabelChanges(Arg, View, {});
}
}
bool handleQualifiedReplacement(Expr* Call) {
auto handleDecl = [&](ValueDecl *VD, SourceRange ToReplace) {
for (auto *I: getRelatedDiffItems(VD)) {
if (auto *Item = dyn_cast<TypeMemberDiffItem>(I)) {
if (Item->Subkind == TypeMemberDiffItemSubKind::QualifiedReplacement) {
bool NeedNoTypeName = isDotMember(ToReplace) &&
Item->oldPrintedName == Item->newPrintedName;
if (NeedNoTypeName) {
Editor.replace(ToReplace,
(llvm::Twine(Item->isNewNameGlobal() ? "" : ".") +
Item->getNewName().base()).str());
} else {
Editor.replace(ToReplace,
(llvm::Twine(Item->getNewTypeAndDot()) +
Item->getNewName().base()).str());
}
return true;
}
}
}
return false;
};
if (auto *DSC = dyn_cast<DotSyntaxCallExpr>(Call)) {
if (auto FD = DSC->getFn()->getReferencedDecl().getDecl()) {
if (handleDecl(FD, Call->getSourceRange()))
return true;
}
} else if (auto MRE = dyn_cast<MemberRefExpr>(Call)) {
if (handleDecl(MRE->getReferencedDecl().getDecl(), MRE->getSourceRange()))
return true;
}
return false;
}
bool handleSpecialCases(ValueDecl *FD, CallExpr* Call, Expr *Arg) {
SpecialCaseDiffItem *Item = nullptr;
for (auto *I: getRelatedDiffItems(FD)) {
Item = dyn_cast<SpecialCaseDiffItem>(I);
if (Item)
break;
}
if (!Item)
return false;
std::vector<CallArgInfo> AllArgs =
getCallArgInfo(SM, Arg, LabelRangeEndAt::LabelNameOnly);
switch(Item->caseId) {
case SpecialCaseId::NSOpenGLSetOption: {
// swift 3.2:
// NSOpenGLSetOption(NSOpenGLGOFormatCacheSize, 5)
// swift 4:
// NSOpenGLGOFormatCacheSize.globalValue = 5
CallArgInfo &FirstArg = AllArgs[0];
CallArgInfo &SecondArg = AllArgs[1];
Editor.remove(CharSourceRange(SM, Call->getStartLoc(),
FirstArg.ArgExp->getStartLoc()));
Editor.replace(CharSourceRange(SM, Lexer::getLocForEndOfToken(SM,
FirstArg.ArgExp->getEndLoc()), SecondArg.LabelRange.getStart()),
".globalValue = ");
Editor.remove(Call->getEndLoc());
return true;
}
case SpecialCaseId::NSOpenGLGetOption: {
// swift 3.2:
// NSOpenGLGetOption(NSOpenGLGOFormatCacheSize, &cacheSize)
// swift 4:
// cacheSize = NSOpenGLGOFormatCacheSize.globalValue
CallArgInfo &FirstArg = AllArgs[0];
CallArgInfo &SecondArg = AllArgs[1];
StringRef SecondArgContent = SecondArg.getEntireCharRange(SM).str();
if (SecondArgContent[0] == '&')
SecondArgContent = SecondArgContent.substr(1);
Editor.replace(CharSourceRange(SM, Call->getStartLoc(),
FirstArg.ArgExp->getStartLoc()),
(llvm::Twine(SecondArgContent) + " = ").str());
Editor.replace(CharSourceRange(SM, FirstArg.getEntireCharRange(SM).getEnd(),
Lexer::getLocForEndOfToken(SM, Call->getEndLoc())),
".globalValue");
return true;
}
case SpecialCaseId::StaticAbsToSwiftAbs:
// swift 3:
// CGFloat.abs(1.0)
// Float.abs(1.0)
// Double.abs(1.0)
// Float80.abs(1.0)
//
// swift 4:
// Swift.abs(1.0)
// Swift.abs(1.0)
// Swift.abs(1.0)
// Swift.abs(1.0)
Editor.replace(Call->getFn()->getSourceRange(), "Swift.abs");
return true;
case SpecialCaseId::ToUIntMax:
if (const auto *DotCall = dyn_cast<DotSyntaxCallExpr>(Call->getFn())) {
Editor.insert(DotCall->getStartLoc(), "UInt64(");
Editor.replace({ DotCall->getDotLoc(), Call->getEndLoc() }, ")");
return true;
}
return false;
case SpecialCaseId::ToIntMax:
if (const auto *DotCall = dyn_cast<DotSyntaxCallExpr>(Call->getFn())) {
Editor.insert(DotCall->getStartLoc(), "Int64(");
Editor.replace({ DotCall->getDotLoc(), Call->getEndLoc() }, ")");
return true;
}
return false;
case SpecialCaseId::NSOpenGLGetVersion: {
if (const auto *Tuple = dyn_cast<TupleExpr>(Arg)) {
if (Tuple->getNumElements() != 2) {
return false;
}
auto extractArg = [](const Expr *Arg) -> const DeclRefExpr * {
while (const auto *ICE = dyn_cast<ImplicitConversionExpr>(Arg)) {
Arg = ICE->getSubExpr();
}
if (const auto *IOE = dyn_cast<InOutExpr>(Arg)) {
return dyn_cast<DeclRefExpr>(IOE->getSubExpr());
}
return nullptr;
};
const auto *Arg0 = extractArg(Tuple->getElement(0));
const auto *Arg1 = extractArg(Tuple->getElement(1));
if (!(Arg0 && Arg1)) {
return false;
}
SmallString<256> Scratch;
llvm::raw_svector_ostream OS(Scratch);
auto StartLoc = Call->getStartLoc();
Editor.insert(StartLoc, "(");
Editor.insert(StartLoc,
SM.extractText(Lexer::getCharSourceRangeFromSourceRange(SM,
Arg0->getSourceRange())));
Editor.insert(StartLoc, ", ");
Editor.insert(StartLoc,
SM.extractText(Lexer::getCharSourceRangeFromSourceRange(SM,
Arg1->getSourceRange())));
Editor.insert(StartLoc, ") = ");
Editor.replace(Call->getSourceRange(), "NSOpenGLContext.openGLVersion");
return true;
}
return false;
}
case SpecialCaseId::UIApplicationMain: {
// If the first argument is CommandLine.argc, replace the second argument
// with CommandLine.unsafeArgv
CallArgInfo &FirstArg = AllArgs[0];
// handle whitespace/line splits around the first arg when matching
auto FirstArgSplit =
SM.extractText(FirstArg.getEntireCharRange(SM)).rsplit('.');
if (!FirstArgSplit.second.empty() &&
FirstArgSplit.first.trim() == "CommandLine" &&
FirstArgSplit.second.trim() == "argc") {
CallArgInfo &SecondArg = AllArgs[1];
Editor.replace(SecondArg.getEntireCharRange(SM),
"CommandLine.unsafeArgv");
return true;
}
return false;
}
}
llvm_unreachable("unhandled case");
}
bool handleTypeHoist(ValueDecl *FD, CallExpr* Call, Expr *Arg) {
TypeMemberDiffItem *Item = nullptr;
for (auto *I: getRelatedDiffItems(FD)) {
Item = dyn_cast<TypeMemberDiffItem>(I);
if (Item)
break;
}
if (!Item)
return false;
if (Item->Subkind == TypeMemberDiffItemSubKind::SimpleReplacement ||
Item->Subkind == TypeMemberDiffItemSubKind::QualifiedReplacement ||
Item->Subkind == TypeMemberDiffItemSubKind::FuncRename)
return false;
if (Item->Subkind == TypeMemberDiffItemSubKind::GlobalFuncToStaticProperty) {
Editor.replace(Call->getSourceRange(),
(llvm::Twine(Item->getNewTypeAndDot()) + Item->getNewName().base()).str());
return true;
}
if (*Item->selfIndex)
return false;
std::vector<CallArgInfo> AllArgs =
getCallArgInfo(SM, Arg, LabelRangeEndAt::LabelNameOnly);
if (!AllArgs.size())
return false;
assert(*Item->selfIndex == 0 && "we cannot handle otherwise");
DeclNameViewer NewName = Item->getNewName();
llvm::SmallVector<unsigned, 2> IgnoredArgIndices;
IgnoredArgIndices.push_back(*Item->selfIndex);
if (auto RI = Item->removedIndex)
IgnoredArgIndices.push_back(*RI);
emitRenameLabelChanges(Arg, NewName, IgnoredArgIndices);
auto *SelfExpr = AllArgs[0].ArgExp;
if (auto *IOE = dyn_cast<InOutExpr>(SelfExpr))
SelfExpr = IOE->getSubExpr();
const bool NeedParen = !SelfExpr->canAppendPostfixExpression();
// Remove the global function name: "Foo(a, b..." to "a, b..."
Editor.remove(CharSourceRange(SM, Call->getStartLoc(),
SelfExpr->getStartLoc()));
if (NeedParen)
Editor.insert(SelfExpr->getStartLoc(), "(");
std::string MemberFuncBase;
if (Item->Subkind == TypeMemberDiffItemSubKind::HoistSelfAndUseProperty)
MemberFuncBase = (llvm::Twine(NeedParen ? ")." : ".") + Item->getNewName().
base()).str();
else
MemberFuncBase = (llvm::Twine(NeedParen ? ")." : ".") + Item->getNewName().
base() + "(").str();
if (AllArgs.size() > 1) {
Editor.replace(CharSourceRange(SM, Lexer::getLocForEndOfToken(SM,
SelfExpr->getEndLoc()), AllArgs[1].LabelRange.getStart()),
MemberFuncBase);
} else {
Editor.insert(Lexer::getLocForEndOfToken(SM, SelfExpr->getEndLoc()),
MemberFuncBase);
}
switch (Item->Subkind) {
case TypeMemberDiffItemSubKind::FuncRename:
case TypeMemberDiffItemSubKind::GlobalFuncToStaticProperty:
case TypeMemberDiffItemSubKind::SimpleReplacement:
case TypeMemberDiffItemSubKind::QualifiedReplacement:
llvm_unreachable("should be handled elsewhere");
case TypeMemberDiffItemSubKind::HoistSelfOnly:
// we are done here.
return true;
case TypeMemberDiffItemSubKind::HoistSelfAndRemoveParam: {
unsigned RI = *Item->removedIndex;
CallArgInfo &ToRemove = AllArgs[RI];
if (AllArgs.size() == RI + 1) {
Editor.remove(ToRemove.getEntireCharRange(SM));
} else {
CallArgInfo &AfterToRemove = AllArgs[RI + 1];
Editor.remove(CharSourceRange(SM, ToRemove.LabelRange.getStart(),
AfterToRemove.LabelRange.getStart()));
}
return true;
}
case TypeMemberDiffItemSubKind::HoistSelfAndUseProperty:
// Remove ).
Editor.remove(Arg->getEndLoc());
return true;
}
llvm_unreachable("unhandled subkind");
}
void handleFunctionCallToPropertyChange(ValueDecl *FD, Expr* FuncRefContainer,
Expr *Arg) {
for (auto *Item : getRelatedDiffItems(FD)) {
if (auto *CD = dyn_cast<CommonDiffItem>(Item)) {
switch (CD->DiffKind) {
case NodeAnnotation::GetterToProperty: {
// Remove "()"
Editor.remove(Lexer::getCharSourceRangeFromSourceRange(SM,
Arg->getSourceRange()));
return;
}
case NodeAnnotation::SetterToProperty: {
ReferenceCollector Walker(FD);
Walker.walk(FuncRefContainer);
auto ReplaceRange = CharSourceRange(SM, Walker.Result.getStart(),
Arg->getStartLoc().getAdvancedLoc(1));
// Replace "x.getY(" with "x.Y =".
auto Replacement = (llvm::Twine(Walker.Result.str()
.substr(3)) + " = ").str();
SmallString<64> Scratch;
Editor.replace(ReplaceRange,
camel_case::toLowercaseInitialisms(Replacement, Scratch));
// Remove ")"
Editor.remove(CharSourceRange(SM, Arg->getEndLoc(), Arg->getEndLoc().
getAdvancedLoc(1)));
return;
}
default:
break;
}
}
}
}
void replaceExpr(Expr* E, StringRef Text) {
Editor.replace(CharSourceRange(SM, E->getStartLoc(),
Lexer::getLocForEndOfToken(SM, E->getEndLoc())), Text);
}
bool wrapAttributeReference(Expr* Reference, Expr* WrapperTarget,
bool FromString) {
auto *RD = getReferencedDecl(Reference);
if (!RD)
return false;
std::string Rename;
Optional<NodeAnnotation> Kind;
StringRef LeftComment;
StringRef RightComment;
for (auto *Item: getRelatedDiffItems(RD)) {
if (isSimpleReplacement(Item, isDotMember(Reference), Rename)) {
} else if (auto *CI = dyn_cast<CommonDiffItem>(Item)) {
if (CI->isStringRepresentableChange() &&
CI->NodeKind == SDKNodeKind::DeclVar) {
Kind = CI->DiffKind;
LeftComment = CI->LeftComment;
RightComment = CI->RightComment;
}
}
}
if (!Kind.hasValue())
return false;
if (Kind) {
insertHelperFunction(*Kind, LeftComment, RightComment, FromString,
WrapperTarget);
}
if (!Rename.empty()) {
replaceExpr(Reference, Rename);
}
return true;
}
bool handleAssignDestMigration(Expr *E) {
auto *ASE = dyn_cast<AssignExpr>(E);
if (!ASE || !ASE->getDest() || !ASE->getSrc())
return false;
auto *Dest = ASE->getDest();
auto Src = ASE->getSrc();
if (wrapAttributeReference(Dest, Src, true)) {
// We should handle the assignment source here since we won't visit
// the children with present changes.
handleAttributeReference(Src);
return true;
}
return false;
}
bool handleAttributeReference(Expr *E) {
return wrapAttributeReference(E, E, false);
}
ConversionFunctionInfo &insertHelperFunction(NodeAnnotation Anno,
StringRef RawType,
StringRef NewType,
bool FromString,
Expr *Wrappee) {
HelperFuncInfo.emplace_back(Wrappee);
ConversionFunctionInfo &Info = HelperFuncInfo.back();
llvm::raw_svector_ostream OS(Info.Buffer);
OS << "\n";
OS << "// Helper function inserted by Swift 4.2 migrator.\n";
OS << "fileprivate func ";
Info.FuncNameStart = Info.Buffer.size();
OS << (FromString ? "convertTo" : "convertFrom");
SmallVector<std::string, 8> Segs;
StringRef guard = "\tguard let input = input else { return nil }\n";
switch(Anno) {
case NodeAnnotation::OptionalArrayMemberUpdate:
Segs = {"Optional", "Array", (Twine("[") + RawType + "]?").str()};
Segs.push_back((Twine("[") + NewType +"]?").str());
Segs.push_back((Twine(guard) + "\treturn input.map { key in " + NewType +"(key) }").str());
Segs.push_back((Twine(guard) + "\treturn input.map { key in key.rawValue }").str());
break;
case NodeAnnotation::OptionalDictionaryKeyUpdate:
Segs = {"Optional", "Dictionary", (Twine("[") + RawType + ": Any]?").str()};
Segs.push_back((Twine("[") + NewType +": Any]?").str());
Segs.push_back((Twine(guard) +
"\treturn Dictionary(uniqueKeysWithValues: input.map"
" { key, value in (" + NewType + "(rawValue: key), value)})").str());
Segs.push_back((Twine(guard) +
"\treturn Dictionary(uniqueKeysWithValues: input.map"
" {key, value in (key.rawValue, value)})").str());
break;
case NodeAnnotation::ArrayMemberUpdate:
Segs = {"", "Array", (Twine("[") + RawType + "]").str()};
Segs.push_back((Twine("[") + NewType +"]").str());
Segs.push_back((Twine("\treturn input.map { key in ") + NewType +"(key) }").str());
Segs.push_back("\treturn input.map { key in key.rawValue }");
break;
case NodeAnnotation::DictionaryKeyUpdate:
Segs = {"", "Dictionary", (Twine("[") + RawType + ": Any]").str()};
Segs.push_back((Twine("[") + NewType +": Any]").str());
Segs.push_back((Twine("\treturn Dictionary(uniqueKeysWithValues: input.map"
" { key, value in (") + NewType + "(rawValue: key), value)})").str());
Segs.push_back("\treturn Dictionary(uniqueKeysWithValues: input.map"
" {key, value in (key.rawValue, value)})");
break;
case NodeAnnotation::SimpleStringRepresentableUpdate:
Segs = {"", "", RawType};
Segs.push_back(NewType);
Segs.push_back((Twine("\treturn ") + NewType + "(rawValue: input)").str());
Segs.push_back("\treturn input.rawValue");
break;
case NodeAnnotation::SimpleOptionalStringRepresentableUpdate:
Segs = {"Optional", "", (Twine(RawType) +"?").str()};
Segs.push_back((Twine(NewType) +"?").str());
Segs.push_back((Twine(guard) + "\treturn " + NewType + "(rawValue: input)").str());
Segs.push_back((Twine(guard) + "\treturn input.rawValue").str());
break;
default:
llvm_unreachable("shouldn't handle this key.");
}
assert(Segs.size() == 6);
OS << Segs[0];
SmallVector<StringRef, 4> Parts;
NewType.split(Parts, '.');
for (auto P: Parts)
OS << P;
OS << Segs[1];
Info.FuncNameEnd = Info.Buffer.size();
if (FromString) {
OS << "(_ input: " << Segs[2] << ") -> " << Segs[3] << " {\n";
OS << Segs[4] << "\n}\n";
} else {
OS << "(_ input: " << Segs[3] << ") -> " << Segs[2] << " {\n";
OS << Segs[5] << "\n}\n";
}
return Info;
}
void handleStringRepresentableArg(ValueDecl *FD, Expr *Arg, Expr *Call) {
NodeAnnotation Kind;
StringRef RawType;
StringRef NewAttributeType;
uint8_t ArgIdx;
for (auto Item: getRelatedDiffItems(FD)) {
if (auto *CI = dyn_cast<CommonDiffItem>(Item)) {
if (CI->isStringRepresentableChange()) {
Kind = CI->DiffKind;
RawType = CI->LeftComment;
NewAttributeType = CI->RightComment;
assert(CI->getChildIndices().size() == 1);
ArgIdx = CI->getChildIndices().front();
break;
}
}
}
if (NewAttributeType.empty())
return;
Expr *WrapTarget = Call;
bool FromString = false;
if (ArgIdx) {
ArgIdx --;
FromString = true;
auto AllArgs = getCallArgInfo(SM, Arg, LabelRangeEndAt::LabelNameOnly);
if (AllArgs.size() <= ArgIdx)
return;
WrapTarget = AllArgs[ArgIdx].ArgExp;
}
assert(WrapTarget);
insertHelperFunction(Kind, RawType, NewAttributeType, FromString, WrapTarget);
}
bool hasRevertRawRepresentableChange(ValueDecl *VD) {
for (auto Item: getRelatedDiffItems(VD)) {
if (auto *CI = dyn_cast<CommonDiffItem>(Item)) {
if (CI->DiffKind ==
NodeAnnotation::RevertTypeAliasDeclToRawRepresentable)
return true;
}
}
return false;
}
bool handleRevertRawRepresentable(Expr *E) {
// Change attribute.rawValue to attribute
if (auto *MRE = dyn_cast<MemberRefExpr>(E)) {
auto Found = false;
if (auto *Base = MRE->getBase()) {
if (hasRevertRawRepresentableChange(Base->getType()->getAnyNominal())) {
Found = true;
}
}
if (!Found)
return false;
auto NL = MRE->getNameLoc().getStartLoc();
auto DL = MRE->getDotLoc();
if (NL.isInvalid() || DL.isInvalid())
return false;
CharSourceRange Range = Lexer::getCharSourceRangeFromSourceRange(SM, {DL, NL});
if (Range.str() == ".rawValue") {
Editor.remove(Range);
return true;
}
}
// Change attribute(rawValue: "value") to "value"
// Change attribute("value") to "value"
if (auto *CE = dyn_cast<CallExpr>(E)) {
auto Found = false;
if (auto *CRC = dyn_cast<ConstructorRefCallExpr>(CE->getFn())) {
if (auto *TE = dyn_cast<TypeExpr>(CRC->getBase())) {
if (hasRevertRawRepresentableChange(TE->getInstanceType()->getAnyNominal()))
Found = true;
}
}
if (!Found)
return false;
std::vector<CallArgInfo> AllArgs =
getCallArgInfo(SM, CE->getArg(), LabelRangeEndAt::LabelNameOnly);
if (AllArgs.size() == 1) {
auto Label = AllArgs.front().LabelRange.str();
if (Label == "rawValue" || Label.empty()) {
Editor.replace(CE->getSourceRange(),
Lexer::getCharSourceRangeFromSourceRange(SM,
AllArgs.front().ArgExp->getSourceRange()).str());
return true;
}
}
}
return false;
}
void handleResultTypeChange(ValueDecl *FD, Expr *Call) {
Optional<NodeAnnotation> ChangeKind;
// look for related change item for the function decl.
for (auto Item: getRelatedDiffItems(FD)) {
if (auto *CI = dyn_cast<CommonDiffItem>(Item)) {
// check if the function's return type has been changed from nonnull
// to nullable.
if (CI->DiffKind == NodeAnnotation::WrapOptional &&
CI->getChildIndices().size() == 1 &&
CI->getChildIndices().front() == 0) {
ChangeKind = NodeAnnotation::WrapOptional;
break;
}
}
}
if (!ChangeKind.hasValue())
return;
// If a function's return type has been changed from nonnull to nullable,
// append ! to the original call expression.
if (*ChangeKind == NodeAnnotation::WrapOptional) {
Editor.insertAfterToken(Call->getSourceRange().End, "!");
}
}
// If a property has changed from nonnull to nullable, we should add ! to the
// reference of the property.
bool handlePropertyTypeChange(Expr *E) {
if (auto MRE = dyn_cast<MemberRefExpr>(E)) {
if (auto *VD = MRE->getReferencedDecl().getDecl()) {
for (auto *I: getRelatedDiffItems(VD)) {
if (auto *Item = dyn_cast<CommonDiffItem>(I)) {
if (Item->DiffKind == NodeAnnotation::WrapOptional &&
Item->NodeKind == SDKNodeKind::DeclVar) {
Editor.insertAfterToken(E->getEndLoc(), "!");
return true;
}
}
}
}
}
return false;
}
bool walkToExprPre(Expr *E) override {
if (E->getSourceRange().isInvalid())
return false;
if (handleRevertRawRepresentable(E)) {
// The name may also change, so we should keep visiting.
return true;
}
if (handleQualifiedReplacement(E))
return false;
if (handleAssignDestMigration(E))
return false;
if (handleAttributeReference(E))
return false;
if (handlePropertyTypeChange(E))
return false;
if (auto *CE = dyn_cast<CallExpr>(E)) {
auto Fn = CE->getFn();
auto Args = CE->getArg();
switch (Fn->getKind()) {
case ExprKind::DeclRef: {
if (auto FD = Fn->getReferencedDecl().getDecl()) {
handleFuncRename(FD, Fn, Args);
handleTypeHoist(FD, CE, Args);
handleSpecialCases(FD, CE, Args);
handleStringRepresentableArg(FD, Args, CE);
handleResultTypeChange(FD, CE);
}
break;
}
case ExprKind::DotSyntaxCall: {
auto DSC = cast<DotSyntaxCallExpr>(Fn);
if (auto FD = DSC->getFn()->getReferencedDecl().getDecl()) {
handleFuncRename(FD, DSC->getFn(), Args);
handleFunctionCallToPropertyChange(FD, DSC->getFn(), Args);
handleSpecialCases(FD, CE, Args);
handleStringRepresentableArg(FD, Args, CE);
handleResultTypeChange(FD, CE);
}
break;
}
case ExprKind::ConstructorRefCall: {
auto CCE = cast<ConstructorRefCallExpr>(Fn);
if (auto FD = CCE->getFn()->getReferencedDecl().getDecl()) {
handleFuncRename(FD, CE, Args);
handleStringRepresentableArg(FD, Args, CE);
handleResultTypeChange(FD, CE);
}
break;
}
default:
break;
}
}
return true;
}
static void collectParamters(AbstractFunctionDecl *AFD,
SmallVectorImpl<ParamDecl*> &Results) {
for (auto PD : *AFD->getParameters()) {
Results.push_back(PD);
}
}
void handleFuncDeclRename(AbstractFunctionDecl *AFD,
CharSourceRange NameRange) {
bool IgnoreBase = false;
llvm::SmallString<32> Buffer;
if (auto View = getFuncRename(AFD, Buffer, IgnoreBase)) {
if (!IgnoreBase)
Editor.replace(NameRange, View.base());
unsigned Index = 0;
SmallVector<ParamDecl*, 4> Params;
collectParamters(AFD, Params);
for (auto *PD: Params) {
if (Index == View.argSize())
break;
StringRef NewArg = View.args()[Index++];
auto ArgLoc = PD->getArgumentNameLoc();
// Represent empty label with underscore.
if (NewArg.empty())
NewArg = "_";
// If the argument name is not specified, add the argument name before
// the parameter name.
if (ArgLoc.isInvalid())
Editor.insertBefore(PD->getNameLoc(),
(llvm::Twine(NewArg) + " ").str());
else {
// Otherwise, replace the argument name directly.
Editor.replaceToken(ArgLoc, NewArg);
}
}
}
}
bool typeReplacementMayNeedParens(StringRef Replacement) const {
return Replacement.contains('&') || Replacement.contains("->");
}
void handleOverridingTypeChange(AbstractFunctionDecl *AFD,
CommonDiffItem *DiffItem) {
assert(AFD);
assert(DiffItem->isTypeChange());
ChildIndexFinder Finder(DiffItem->getChildIndices());
auto Result = Finder.findChild(AFD);
if (!Result.isValid())
return;
switch (DiffItem->DiffKind) {
case ide::api::NodeAnnotation::WrapOptional:
if (Result.Suffixable) {
Editor.insertAfterToken(Result.TokenRange.End, "?");
} else {
Editor.insertWrap("(", Result.TokenRange, ")?");
}
break;
case ide::api::NodeAnnotation::WrapImplicitOptional:
if (Result.Suffixable) {
Editor.insertAfterToken(Result.TokenRange.End, "!");
} else {
Editor.insertWrap("(", Result.TokenRange, (")!"));
}
break;
case ide::api::NodeAnnotation::UnwrapOptional:
if (Result.Optional)
Editor.remove(Result.TokenRange.End);
break;
case ide::api::NodeAnnotation::ImplicitOptionalToOptional:
if (Result.Optional)
Editor.replace(Result.TokenRange.End, "?");
break;
case ide::api::NodeAnnotation::TypeRewritten:
Editor.replace(Result.TokenRange, DiffItem->RightComment);
if (Result.Suffixed && typeReplacementMayNeedParens(DiffItem->RightComment)) {
Editor.insertBefore(Result.TokenRange.Start, "(");
Editor.insertAfterToken(Result.TokenRange.End, ")");
}
break;
default:
break;
}
}
void handleOverridingPropertyChange(AbstractFunctionDecl *AFD,
CommonDiffItem *DiffItem) {
assert(AFD);
assert(DiffItem->isToPropertyChange());
auto FD = dyn_cast<FuncDecl>(AFD);
if (!FD)
return;
switch (DiffItem->DiffKind) {
case NodeAnnotation::GetterToProperty: {
auto FuncLoc = FD->getFuncLoc();
auto ReturnTyLoc = FD->getBodyResultTypeLoc().getSourceRange().Start;
auto NameLoc = FD->getNameLoc();
if (FuncLoc.isInvalid() || ReturnTyLoc.isInvalid() || NameLoc.isInvalid())
break;
// Replace "func" with "var"
Editor.replaceToken(FuncLoc, "var");
// Replace "() -> " with ": "
Editor.replace(CharSourceRange(SM, Lexer::getLocForEndOfToken(SM, NameLoc),
ReturnTyLoc), ": ");
break;
}
case NodeAnnotation::SetterToProperty: {
// FIXME: we should migrate this case too.
break;
}
default:
llvm_unreachable("should not be handled here.");
}
}
// When users override a SDK function whose parameter types have been changed,
// we should introduce a local variable in the body of the function definition
// to shadow the changed parameter. Also, a proper conversion function should
// be defined to bridge the parameter to the local variable.
void handleLocalParameterBridge(AbstractFunctionDecl *AFD,
CommonDiffItem *DiffItem) {
assert(AFD);
assert(DiffItem->isStringRepresentableChange());
// We only handle top-level parameter type change.
if (DiffItem->getChildIndices().size() != 1)
return;
auto Idx = DiffItem->getChildIndices().front();
// We don't handle return type change.
if (Idx == 0)
return;
Idx --;
SmallVector<ParamDecl*, 4> Params;
collectParamters(AFD, Params);
if (Params.size() <= Idx)
return;
// Get the internal name of the changed paramter.
auto VariableName = Params[Idx]->getParameterName().str();
// Insert the helper function to convert the type back to raw types.
auto &Info = insertHelperFunction(DiffItem->DiffKind, DiffItem->LeftComment,
DiffItem->RightComment, /*From String*/false,
/*No expression to wrap*/nullptr);
auto BL = AFD->getBodySourceRange().Start;
if (BL.isValid()) {
// Insert the local variable declaration after the opening brace.
Editor.insertAfterToken(BL,
(llvm::Twine("\n// Local variable inserted by Swift 4.2 migrator.") +
"\nlet " + VariableName + " = " + Info.getFuncName() + "(" +
VariableName + ")\n").str());
}
}
llvm::StringSet<> funcNamesForOverrideRemoval() {
llvm::StringSet<> Results;
Results.insert("c:objc(cs)NSObject(im)application:delegateHandlesKey:");
Results.insert("c:objc(cs)NSObject(im)changeColor:");
Results.insert("c:objc(cs)NSObject(im)controlTextDidBeginEditing:");
Results.insert("c:objc(cs)NSObject(im)controlTextDidEndEditing:");
Results.insert("c:objc(cs)NSObject(im)controlTextDidChange:");
Results.insert("c:objc(cs)NSObject(im)changeFont:");
Results.insert("c:objc(cs)NSObject(im)validModesForFontPanel:");
Results.insert("c:objc(cs)NSObject(im)discardEditing");
Results.insert("c:objc(cs)NSObject(im)commitEditing");
Results.insert("c:objc(cs)NSObject(im)commitEditingWithDelegate:didCommitSelector:contextInfo:");
Results.insert("c:objc(cs)NSObject(im)commitEditingAndReturnError:");
Results.insert("c:objc(cs)NSObject(im)objectDidBeginEditing:");
Results.insert("c:objc(cs)NSObject(im)objectDidEndEditing:");
Results.insert("c:objc(cs)NSObject(im)validateMenuItem:");
Results.insert("c:objc(cs)NSObject(im)pasteboard:provideDataForType:");
Results.insert("c:objc(cs)NSObject(im)pasteboardChangedOwner:");
Results.insert("c:objc(cs)NSObject(im)validateToolbarItem:");
Results.insert("c:objc(cs)NSObject(im)layer:shouldInheritContentsScale:fromWindow:");
Results.insert("c:objc(cs)NSObject(im)view:stringForToolTip:point:userData:");
return Results;
}
SourceLoc shouldRemoveOverride(AbstractFunctionDecl *AFD) {
if (AFD->getKind() != DeclKind::Func)
return SourceLoc();
SourceLoc OverrideLoc;
// Get the location of override keyword.
if (auto *Override = AFD->getAttrs().getAttribute<OverrideAttr>()) {
if (Override->getRange().isValid()) {
OverrideLoc = Override->getLocation();
}
}
if (OverrideLoc.isInvalid())
return SourceLoc();
auto *OD = AFD->getOverriddenDecl();
llvm::SmallString<64> Buffer;
llvm::raw_svector_ostream OS(Buffer);
if (swift::ide::printValueDeclUSR(OD, OS))
return SourceLoc();
return OverridingRemoveNames.find(OS.str()) == OverridingRemoveNames.end() ?
SourceLoc() : OverrideLoc;
}
struct SuperRemoval: public ASTWalker {
EditorAdapter &Editor;
llvm::StringSet<> &USRs;
SuperRemoval(EditorAdapter &Editor, llvm::StringSet<> &USRs):
Editor(Editor), USRs(USRs) {}
bool isSuperExpr(Expr *E) {
if (E->isImplicit())
return false;
// Check if the expression is super.foo().
if (auto *CE = dyn_cast<CallExpr>(E)) {
if (auto *DSC = dyn_cast<DotSyntaxCallExpr>(CE->getFn())) {
if (DSC->getBase()->getKind() != ExprKind::SuperRef)
return false;
llvm::SmallString<64> Buffer;
llvm::raw_svector_ostream OS(Buffer);
auto *RD = DSC->getFn()->getReferencedDecl().getDecl();
if (swift::ide::printValueDeclUSR(RD, OS))
return false;
return USRs.find(OS.str()) != USRs.end();
}
}
// We should handle try super.foo() too.
if (auto *TE = dyn_cast<AnyTryExpr>(E)) {
return isSuperExpr(TE->getSubExpr());
}
return false;
}
std::pair<bool, Stmt*> walkToStmtPre(Stmt *S) override {
if (auto *BS = dyn_cast<BraceStmt>(S)) {
for(auto Ele: BS->getElements()) {
if (Ele.is<Expr*>() && isSuperExpr(Ele.get<Expr*>())) {
Editor.remove(Ele.getSourceRange());
}
}
}
// We only handle top-level expressions, so avoid visiting further.
return {false, S};
}
};
bool walkToDeclPre(Decl *D, CharSourceRange Range) override {
if (D->isImplicit())
return true;
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(D)) {
handleFuncDeclRename(AFD, Range);
for (auto *Item: getRelatedDiffItems(AFD)) {
if (auto *DiffItem = dyn_cast<CommonDiffItem>(Item)) {
if (DiffItem->isTypeChange())
handleOverridingTypeChange(AFD, DiffItem);
else if (DiffItem->isToPropertyChange())
handleOverridingPropertyChange(AFD, DiffItem);
else if (DiffItem->isStringRepresentableChange())
handleLocalParameterBridge(AFD, DiffItem);
}
}
auto OverrideLoc = shouldRemoveOverride(AFD);
if (OverrideLoc.isValid()) {
// Remove override keyword.
Editor.remove(OverrideLoc);
// Remove super-dot call.
SuperRemoval Removal(Editor, OverridingRemoveNames);
D->walk(Removal);
}
}
// Handle property overriding migration.
if (auto *VD = dyn_cast<VarDecl>(D)) {
for (auto *Item: getRelatedDiffItems(VD)) {
if (auto *CD = dyn_cast<CommonDiffItem>(Item)) {
// If the overriden property has been renamed, we should rename
// this property decl as well.
if (CD->isRename() && VD->getNameLoc().isValid()) {
Editor.replaceToken(VD->getNameLoc(), CD->getNewName());
}
}
}
}
return true;
}
};
} // end anonymous namespace
void migrator::runAPIDiffMigratorPass(EditorAdapter &Editor,
SourceFile *SF,
const MigratorOptions &Opts) {
APIDiffMigratorPass { Editor, SF, Opts }.run();
}