Skip to content

Commit 8acd794

Browse files
Власов Антон ВячеславовичВласов Антон Вячеславович
authored andcommitted
SR-5744 Refactoring action to convert if-let to guard-let and vice versa
1 parent 749a972 commit 8acd794

File tree

13 files changed

+522
-0
lines changed

13 files changed

+522
-0
lines changed

include/swift/IDE/RefactoringKinds.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ RANGE_REFACTORING(ExpandTernaryExpr, "Expand Ternary Expression", expand.ternary
6666

6767
RANGE_REFACTORING(ConvertToTernaryExpr, "Convert To Ternary Expression", convert.ternary.expr)
6868

69+
RANGE_REFACTORING(ConvertIfLetExprToGuardExpr, "Convert To Guard Expression", convert.iflet.to.guard.expr)
70+
71+
RANGE_REFACTORING(ConvertGuardExprToIfLetExpr, "Convert To IfLet Expression", convert.to.iflet.expr)
72+
6973
// These internal refactorings are designed to be helpful for working on
7074
// the compiler/standard library, etc., but are likely to be just confusing and
7175
// noise for general development.

lib/IDE/Refactoring.cpp

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2068,6 +2068,211 @@ bool RefactoringActionExpandTernaryExpr::performChange() {
20682068
return false; //don't abort
20692069
}
20702070

2071+
bool RefactoringActionConvertIfLetExprToGuardExpr::
2072+
isApplicable(ResolvedRangeInfo Info, DiagnosticEngine &Diag) {
2073+
2074+
if (Info.Kind != RangeKind::SingleStatement
2075+
&& Info.Kind != RangeKind::MultiStatement)
2076+
return false;
2077+
2078+
if (Info.ContainedNodes.empty())
2079+
return false;
2080+
2081+
IfStmt *If = nullptr;
2082+
2083+
if (Info.ContainedNodes.size() == 1) {
2084+
if (auto S = Info.ContainedNodes[0].dyn_cast<Stmt*>()) {
2085+
If = dyn_cast<IfStmt>(S);
2086+
}
2087+
}
2088+
2089+
if (!If)
2090+
return false;
2091+
2092+
auto CondList = If->getCond();
2093+
2094+
if (CondList.size() == 1) {
2095+
auto E = CondList[0];
2096+
auto P = E.getKind();
2097+
if (P == swift::StmtConditionElement::CK_PatternBinding)
2098+
return true;
2099+
}
2100+
2101+
return false;
2102+
}
2103+
2104+
bool RefactoringActionConvertIfLetExprToGuardExpr::performChange() {
2105+
2106+
IfStmt *If = nullptr;
2107+
2108+
if (RangeInfo.ContainedNodes.size() == 1) {
2109+
if (auto S = RangeInfo.ContainedNodes[0].dyn_cast<Stmt*>()) {
2110+
If = dyn_cast<IfStmt>(S);
2111+
}
2112+
}
2113+
2114+
if (!If)
2115+
return true; // abort
2116+
2117+
auto CondList = If->getCond();
2118+
2119+
if (CondList.size() != 1)
2120+
return true; // abort
2121+
2122+
auto E = CondList[0];
2123+
auto P = E.getPatternOrNull();
2124+
if (!P)
2125+
return true; // abort
2126+
2127+
// Get if-let condition
2128+
SourceRange range = E.getSourceRange();
2129+
SourceManager &SM = RangeInfo.RangeContext->getASTContext().SourceMgr;
2130+
auto CondCharRange = Lexer::getCharSourceRangeFromSourceRange(SM, range);
2131+
2132+
auto Body = dyn_cast_or_null<BraceStmt>(If->getThenStmt());
2133+
2134+
if (!Body)
2135+
return true; // abort
2136+
2137+
// Get if-let then body.
2138+
auto firstElement = Body->getElements()[0];
2139+
auto lastElement = Body->getElements().back();
2140+
SourceRange bodyRange = firstElement.getSourceRange();
2141+
bodyRange.widen(lastElement.getSourceRange());
2142+
auto BodyCharRange = Lexer::getCharSourceRangeFromSourceRange(SM, bodyRange);
2143+
2144+
llvm::SmallString<64> DeclBuffer;
2145+
llvm::raw_svector_ostream OS(DeclBuffer);
2146+
2147+
llvm::StringRef Space = " ";
2148+
llvm::StringRef NewLine = "\n";
2149+
2150+
OS << tok::kw_guard << Space;
2151+
OS << CondCharRange.str().str() << Space;
2152+
OS << tok::kw_else << Space;
2153+
OS << tok::l_brace << NewLine;
2154+
2155+
// Get if-let else body.
2156+
auto ElseBody = dyn_cast_or_null<BraceStmt>(If->getElseStmt());
2157+
if (ElseBody) {
2158+
auto firstElseElement = ElseBody->getElements()[0];
2159+
auto lastElseElement = ElseBody->getElements().back();
2160+
SourceRange elseBodyRange = firstElseElement.getSourceRange();
2161+
elseBodyRange.widen(lastElseElement.getSourceRange());
2162+
auto ElseBodyCharRange = Lexer::getCharSourceRangeFromSourceRange(SM, elseBodyRange);
2163+
OS << ElseBodyCharRange.str().str() << NewLine;
2164+
}
2165+
2166+
OS << tok::kw_return << NewLine;
2167+
OS << tok::r_brace << NewLine;
2168+
OS << BodyCharRange.str().str();
2169+
2170+
// Replace if-let to guard
2171+
auto ReplaceRange = RangeInfo.ContentRange;
2172+
EditConsumer.accept(SM, ReplaceRange, DeclBuffer.str());
2173+
2174+
return false;
2175+
}
2176+
2177+
bool RefactoringActionConvertGuardExprToIfLetExpr::
2178+
isApplicable(ResolvedRangeInfo Info, DiagnosticEngine &Diag) {
2179+
if (Info.Kind != RangeKind::SingleStatement
2180+
&& Info.Kind != RangeKind::MultiStatement)
2181+
return false;
2182+
2183+
if (Info.ContainedNodes.empty())
2184+
return false;
2185+
2186+
GuardStmt *guardStmt = nullptr;
2187+
2188+
if (Info.ContainedNodes.size() > 0) {
2189+
if (auto S = Info.ContainedNodes[0].dyn_cast<Stmt*>()) {
2190+
guardStmt = dyn_cast<GuardStmt>(S);
2191+
}
2192+
}
2193+
2194+
if (!guardStmt)
2195+
return false;
2196+
2197+
auto CondList = guardStmt->getCond();
2198+
2199+
if (CondList.size() == 1) {
2200+
auto E = CondList[0];
2201+
auto P = E.getKind();
2202+
if (P == swift::StmtConditionElement::CK_PatternBinding)
2203+
return true;
2204+
}
2205+
2206+
return false;
2207+
}
2208+
2209+
bool RefactoringActionConvertGuardExprToIfLetExpr::performChange() {
2210+
2211+
// Get guard stmt
2212+
GuardStmt *Guard = nullptr;
2213+
if (RangeInfo.ContainedNodes.size() > 0) {
2214+
if (auto S = RangeInfo.ContainedNodes[0].dyn_cast<Stmt*>()) {
2215+
Guard = dyn_cast<GuardStmt>(S);
2216+
}
2217+
}
2218+
if (!Guard)
2219+
return true; // abort
2220+
2221+
// Get guard condition
2222+
auto CondList = Guard->getCond();
2223+
if (CondList.size() != 1)
2224+
return true; // abort
2225+
2226+
auto E = CondList[0];
2227+
auto P = E.getPatternOrNull();
2228+
if (!P)
2229+
return true; // abort
2230+
2231+
// Get guard condition source
2232+
SourceRange range = E.getSourceRange();
2233+
SourceManager &SM = RangeInfo.RangeContext->getASTContext().SourceMgr;
2234+
auto CondCharRange = Lexer::getCharSourceRangeFromSourceRange(SM, range);
2235+
2236+
llvm::SmallString<64> DeclBuffer;
2237+
llvm::raw_svector_ostream OS(DeclBuffer);
2238+
2239+
llvm::StringRef Space = " ";
2240+
llvm::StringRef NewLine = "\n";
2241+
2242+
OS << tok::kw_if << Space;
2243+
OS << CondCharRange.str().str() << Space;
2244+
OS << tok::l_brace << NewLine;
2245+
2246+
// Get nodes after guard to place them at if-let body
2247+
if (RangeInfo.ContainedNodes.size() > 1) {
2248+
auto S = RangeInfo.ContainedNodes[1].getSourceRange();
2249+
S.widen(RangeInfo.ContainedNodes.back().getSourceRange());
2250+
auto BodyCharRange = Lexer::getCharSourceRangeFromSourceRange(SM, S);
2251+
OS << BodyCharRange.str().str() << NewLine;
2252+
}
2253+
OS << tok::r_brace;
2254+
2255+
// Get guard body
2256+
auto Body = dyn_cast_or_null<BraceStmt>(Guard->getBody());
2257+
2258+
if (Body && Body->getElements().size() > 1) {
2259+
auto firstElement = Body->getElements()[0];
2260+
auto lastElement = Body->getElements().back();
2261+
SourceRange bodyRange = firstElement.getSourceRange();
2262+
bodyRange.widen(lastElement.getSourceRange());
2263+
auto BodyCharRange = Lexer::getCharSourceRangeFromSourceRange(SM, bodyRange);
2264+
OS << Space << tok::kw_else << Space << tok::l_brace << NewLine;
2265+
OS << BodyCharRange.str().str() << NewLine;
2266+
OS << tok::r_brace;
2267+
}
2268+
2269+
// Replace guard to if-let
2270+
auto ReplaceRange = RangeInfo.ContentRange;
2271+
EditConsumer.accept(SM, ReplaceRange, DeclBuffer.str());
2272+
2273+
return false;
2274+
}
2275+
20712276
/// Struct containing info about an IfStmt that can be converted into an IfExpr.
20722277
struct ConvertToTernaryExprInfo {
20732278
ConvertToTernaryExprInfo() {}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
func foo(idxOpt: Int?) {
2+
if let idx = idxOpt {
3+
print(idx)
4+
}
5+
}
6+
7+
func bar(fooOpt: Int?) {
8+
if let foo = fooOpt {
9+
let incrementedFoo = foo + 1
10+
print(incrementedFoo)
11+
}
12+
}
13+
14+
func fooBar(fooOpt: Int?) {
15+
guard let foo = fooOpt else {
16+
return
17+
}
18+
let incrementedFoo = foo + 1
19+
print(incrementedFoo)
20+
print(foo)
21+
}
22+
23+
func barFoo(idxOpt: Int?) {
24+
if let idx = idxOpt {
25+
print(idx)
26+
} else {
27+
print("nil")
28+
}
29+
}
30+
31+
32+
33+
34+
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
func foo(idxOpt: Int?) {
2+
guard let idx = idxOpt else {
3+
return
4+
}
5+
print(idx)
6+
}
7+
8+
func bar(fooOpt: Int?) {
9+
if let foo = fooOpt {
10+
let incrementedFoo = foo + 1
11+
print(incrementedFoo)
12+
}
13+
}
14+
15+
func fooBar(fooOpt: Int?) {
16+
if let foo = fooOpt {
17+
let incrementedFoo = foo + 1
18+
print(incrementedFoo)
19+
print(foo)
20+
}
21+
}
22+
23+
func barFoo(idxOpt: Int?) {
24+
if let idx = idxOpt {
25+
print(idx)
26+
} else {
27+
print("nil")
28+
}
29+
}
30+
31+
32+
33+
34+
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
func foo(idxOpt: Int?) {
2+
if let idx = idxOpt {
3+
print(idx)
4+
}
5+
}
6+
7+
func bar(fooOpt: Int?) {
8+
if let foo = fooOpt {
9+
let incrementedFoo = foo + 1
10+
print(incrementedFoo)
11+
}
12+
}
13+
14+
func fooBar(fooOpt: Int?) {
15+
if let foo = fooOpt {
16+
let incrementedFoo = foo + 1
17+
print(incrementedFoo)
18+
print(foo)
19+
}
20+
}
21+
22+
func barFoo(idxOpt: Int?) {
23+
guard let idx = idxOpt else {
24+
print("nil")
25+
return
26+
}
27+
print(idx)
28+
}
29+
30+
31+
32+
33+
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
func foo(idxOpt: Int?) {
2+
if let idx = idxOpt {
3+
print(idx)
4+
}
5+
}
6+
7+
func bar(fooOpt: Int?) {
8+
guard let foo = fooOpt else {
9+
return
10+
}
11+
let incrementedFoo = foo + 1
12+
print(incrementedFoo)
13+
}
14+
15+
func fooBar(fooOpt: Int?) {
16+
if let foo = fooOpt {
17+
let incrementedFoo = foo + 1
18+
print(incrementedFoo)
19+
print(foo)
20+
}
21+
}
22+
23+
func barFoo(idxOpt: Int?) {
24+
if let idx = idxOpt {
25+
print(idx)
26+
} else {
27+
print("nil")
28+
}
29+
}
30+
31+
32+
33+
34+

0 commit comments

Comments
 (0)