|
| 1 | +//===----------------------------------------------------------------------===// |
| 2 | +// |
| 3 | +// This source file is part of the Swift.org open source project |
| 4 | +// |
| 5 | +// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors |
| 6 | +// Licensed under Apache License v2.0 with Runtime Library Exception |
| 7 | +// |
| 8 | +// See https://swift.org/LICENSE.txt for license information |
| 9 | +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors |
| 10 | +// |
| 11 | +//===----------------------------------------------------------------------===// |
| 12 | + |
| 13 | +#include "ContextFinder.h" |
| 14 | +#include "RefactoringActions.h" |
| 15 | +#include "swift/AST/Stmt.h" |
| 16 | + |
| 17 | +using namespace swift::refactoring; |
| 18 | + |
| 19 | +static CharSourceRange |
| 20 | +findSourceRangeToWrapInCatch(const ResolvedExprStartCursorInfo &CursorInfo, |
| 21 | + SourceFile *TheFile, SourceManager &SM) { |
| 22 | + Expr *E = CursorInfo.getTrailingExpr(); |
| 23 | + if (!E) |
| 24 | + return CharSourceRange(); |
| 25 | + auto Node = ASTNode(E); |
| 26 | + auto NodeChecker = [](ASTNode N) { return N.isStmt(StmtKind::Brace); }; |
| 27 | + ContextFinder Finder(*TheFile, Node, NodeChecker); |
| 28 | + Finder.resolve(); |
| 29 | + auto Contexts = Finder.getContexts(); |
| 30 | + if (Contexts.empty()) |
| 31 | + return CharSourceRange(); |
| 32 | + auto TargetNode = Contexts.back(); |
| 33 | + BraceStmt *BStmt = dyn_cast<BraceStmt>(TargetNode.dyn_cast<Stmt *>()); |
| 34 | + auto ConvertToCharRange = [&SM](SourceRange SR) { |
| 35 | + return Lexer::getCharSourceRangeFromSourceRange(SM, SR); |
| 36 | + }; |
| 37 | + assert(BStmt); |
| 38 | + auto ExprRange = ConvertToCharRange(E->getSourceRange()); |
| 39 | + // Check elements of the deepest BraceStmt, pick one that covers expression. |
| 40 | + for (auto Elem : BStmt->getElements()) { |
| 41 | + auto ElemRange = ConvertToCharRange(Elem.getSourceRange()); |
| 42 | + if (ElemRange.contains(ExprRange)) |
| 43 | + TargetNode = Elem; |
| 44 | + } |
| 45 | + return ConvertToCharRange(TargetNode.getSourceRange()); |
| 46 | +} |
| 47 | + |
| 48 | +bool RefactoringActionConvertToDoCatch::isApplicable(ResolvedCursorInfoPtr Tok, |
| 49 | + DiagnosticEngine &Diag) { |
| 50 | + auto ExprStartInfo = dyn_cast<ResolvedExprStartCursorInfo>(Tok); |
| 51 | + if (!ExprStartInfo || !ExprStartInfo->getTrailingExpr()) |
| 52 | + return false; |
| 53 | + return isa<ForceTryExpr>(ExprStartInfo->getTrailingExpr()); |
| 54 | +} |
| 55 | + |
| 56 | +bool RefactoringActionConvertToDoCatch::performChange() { |
| 57 | + auto ExprStartInfo = cast<ResolvedExprStartCursorInfo>(CursorInfo); |
| 58 | + auto *TryExpr = dyn_cast<ForceTryExpr>(ExprStartInfo->getTrailingExpr()); |
| 59 | + assert(TryExpr); |
| 60 | + auto Range = findSourceRangeToWrapInCatch(*ExprStartInfo, TheFile, SM); |
| 61 | + if (!Range.isValid()) |
| 62 | + return true; |
| 63 | + // Wrap given range in do catch block. |
| 64 | + EditConsumer.accept(SM, Range.getStart(), "do {\n"); |
| 65 | + EditorConsumerInsertStream OS(EditConsumer, SM, Range.getEnd()); |
| 66 | + OS << "\n} catch {\n" << getCodePlaceholder() << "\n}"; |
| 67 | + |
| 68 | + // Delete ! from try! expression |
| 69 | + auto ExclaimLen = getKeywordLen(tok::exclaim_postfix); |
| 70 | + auto ExclaimRange = CharSourceRange(TryExpr->getExclaimLoc(), ExclaimLen); |
| 71 | + EditConsumer.remove(SM, ExclaimRange); |
| 72 | + return false; |
| 73 | +} |
0 commit comments