mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
We were trying to get fancy rewriting cases where multiple 'if let' clauses were used to unwrap a double optional, but in testing that turned out to be too unreliable. Now we simply detect any 'try?' expressions whose behavior will change, and add an explicit 'as OldType' to the expression, in order to preserve the original behavior.
97 lines
3.3 KiB
C++
97 lines
3.3 KiB
C++
//===--- OptionalTryMigratorPass.cpp -------------------------------------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2018 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/ASTVisitor.h"
|
|
#include "swift/AST/Expr.h"
|
|
#include "swift/AST/Module.h"
|
|
#include "swift/AST/NameLookup.h"
|
|
#include "swift/AST/ParameterList.h"
|
|
#include "swift/AST/Types.h"
|
|
#include "swift/IDE/SourceEntityWalker.h"
|
|
#include "swift/Migrator/ASTMigratorPass.h"
|
|
#include "swift/Parse/Lexer.h"
|
|
|
|
using namespace swift;
|
|
using namespace swift::migrator;
|
|
|
|
namespace {
|
|
|
|
class OptionalTryMigratorPass: public ASTMigratorPass,
|
|
public SourceEntityWalker {
|
|
|
|
bool explicitCastActiveForOptionalTry = false;
|
|
|
|
bool walkToExprPre(Expr *E) override {
|
|
if (dyn_cast<ParenExpr>(E) || E->isImplicit()) {
|
|
// Look through parentheses and implicit expressions.
|
|
return true;
|
|
}
|
|
|
|
if (const auto *explicitCastExpr = dyn_cast<ExplicitCastExpr>(E)) {
|
|
// If the user has already provided an explicit cast for the
|
|
// 'try?', then we don't need to add one. So let's track whether
|
|
// one is active
|
|
explicitCastActiveForOptionalTry = true;
|
|
}
|
|
else if (const auto *optTryExpr = dyn_cast<OptionalTryExpr>(E)) {
|
|
wrapTryInCastIfNeeded(optTryExpr);
|
|
return false;
|
|
}
|
|
else if (explicitCastActiveForOptionalTry) {
|
|
// If an explicit cast is active and we are entering a new
|
|
// expression that is not an OptionalTryExpr, then the cast
|
|
// does not apply to the OptionalTryExpr.
|
|
explicitCastActiveForOptionalTry = false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool walkToExprPost(Expr *E) override {
|
|
explicitCastActiveForOptionalTry = false;
|
|
return true;
|
|
}
|
|
|
|
void wrapTryInCastIfNeeded(const OptionalTryExpr *optTryExpr) {
|
|
if (explicitCastActiveForOptionalTry) {
|
|
// There's already an explicit cast here; we don't need to add anything
|
|
return;
|
|
}
|
|
|
|
if (!optTryExpr->getSubExpr()->getType()->getOptionalObjectType()) {
|
|
// This 'try?' doesn't wrap an optional, so its behavior does not
|
|
// change from Swift 4 to Swift 5
|
|
return;
|
|
}
|
|
|
|
Type typeToPreserve = optTryExpr->getType();
|
|
auto typeName = typeToPreserve->getStringAsComponent();
|
|
|
|
auto range = optTryExpr->getSourceRange();
|
|
auto charRange = Lexer::getCharSourceRangeFromSourceRange(SM, range);
|
|
Editor.insertWrap("((", charRange, (Twine(") as ") + typeName + ")").str());
|
|
}
|
|
|
|
public:
|
|
OptionalTryMigratorPass(EditorAdapter &Editor,
|
|
SourceFile *SF,
|
|
const MigratorOptions &Opts)
|
|
: ASTMigratorPass(Editor, SF, Opts) {}
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
void migrator::runOptionalTryMigratorPass(EditorAdapter &Editor,
|
|
SourceFile *SF,
|
|
const MigratorOptions &Opts) {
|
|
OptionalTryMigratorPass { Editor, SF, Opts }.walk(SF);
|
|
}
|