Skip to content

Commit d0f0069

Browse files
authored
Merge pull request #24566 from Regno/feature/vlasov/SR-5744
[SR-5744] Refactoring action to convert if-let to guard-let and vice versa
2 parents d2a0757 + f119f21 commit d0f0069

File tree

13 files changed

+491
-0
lines changed

13 files changed

+491
-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: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2068,6 +2068,180 @@ 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+
auto Body = dyn_cast_or_null<BraceStmt>(If->getThenStmt());
2099+
if (Body)
2100+
return true;
2101+
}
2102+
}
2103+
2104+
return false;
2105+
}
2106+
2107+
bool RefactoringActionConvertIfLetExprToGuardExpr::performChange() {
2108+
2109+
auto S = RangeInfo.ContainedNodes[0].dyn_cast<Stmt*>();
2110+
IfStmt *If = dyn_cast<IfStmt>(S);
2111+
auto CondList = If->getCond();
2112+
2113+
// Get if-let condition
2114+
SourceRange range = CondList[0].getSourceRange();
2115+
SourceManager &SM = RangeInfo.RangeContext->getASTContext().SourceMgr;
2116+
auto CondCharRange = Lexer::getCharSourceRangeFromSourceRange(SM, range);
2117+
2118+
auto Body = dyn_cast_or_null<BraceStmt>(If->getThenStmt());
2119+
2120+
// Get if-let then body.
2121+
auto firstElement = Body->getElements()[0];
2122+
auto lastElement = Body->getElements().back();
2123+
SourceRange bodyRange = firstElement.getSourceRange();
2124+
bodyRange.widen(lastElement.getSourceRange());
2125+
auto BodyCharRange = Lexer::getCharSourceRangeFromSourceRange(SM, bodyRange);
2126+
2127+
llvm::SmallString<64> DeclBuffer;
2128+
llvm::raw_svector_ostream OS(DeclBuffer);
2129+
2130+
llvm::StringRef Space = " ";
2131+
llvm::StringRef NewLine = "\n";
2132+
2133+
OS << tok::kw_guard << Space;
2134+
OS << CondCharRange.str().str() << Space;
2135+
OS << tok::kw_else << Space;
2136+
OS << tok::l_brace << NewLine;
2137+
2138+
// Get if-let else body.
2139+
if (auto *ElseBody = dyn_cast_or_null<BraceStmt>(If->getElseStmt())) {
2140+
auto firstElseElement = ElseBody->getElements()[0];
2141+
auto lastElseElement = ElseBody->getElements().back();
2142+
SourceRange elseBodyRange = firstElseElement.getSourceRange();
2143+
elseBodyRange.widen(lastElseElement.getSourceRange());
2144+
auto ElseBodyCharRange = Lexer::getCharSourceRangeFromSourceRange(SM, elseBodyRange);
2145+
OS << ElseBodyCharRange.str().str() << NewLine;
2146+
}
2147+
2148+
OS << tok::kw_return << NewLine;
2149+
OS << tok::r_brace << NewLine;
2150+
OS << BodyCharRange.str().str();
2151+
2152+
// Replace if-let to guard
2153+
auto ReplaceRange = RangeInfo.ContentRange;
2154+
EditConsumer.accept(SM, ReplaceRange, DeclBuffer.str());
2155+
2156+
return false;
2157+
}
2158+
2159+
bool RefactoringActionConvertGuardExprToIfLetExpr::
2160+
isApplicable(ResolvedRangeInfo Info, DiagnosticEngine &Diag) {
2161+
if (Info.Kind != RangeKind::SingleStatement
2162+
&& Info.Kind != RangeKind::MultiStatement)
2163+
return false;
2164+
2165+
if (Info.ContainedNodes.empty())
2166+
return false;
2167+
2168+
GuardStmt *guardStmt = nullptr;
2169+
2170+
if (Info.ContainedNodes.size() > 0) {
2171+
if (auto S = Info.ContainedNodes[0].dyn_cast<Stmt*>()) {
2172+
guardStmt = dyn_cast<GuardStmt>(S);
2173+
}
2174+
}
2175+
2176+
if (!guardStmt)
2177+
return false;
2178+
2179+
auto CondList = guardStmt->getCond();
2180+
2181+
if (CondList.size() == 1) {
2182+
auto E = CondList[0];
2183+
auto P = E.getPatternOrNull();
2184+
if (P && E.getKind() == swift::StmtConditionElement::CK_PatternBinding)
2185+
return true;
2186+
}
2187+
2188+
return false;
2189+
}
2190+
2191+
bool RefactoringActionConvertGuardExprToIfLetExpr::performChange() {
2192+
2193+
// Get guard stmt
2194+
auto S = RangeInfo.ContainedNodes[0].dyn_cast<Stmt*>();
2195+
GuardStmt *Guard = dyn_cast<GuardStmt>(S);
2196+
2197+
// Get guard condition
2198+
auto CondList = Guard->getCond();
2199+
2200+
// Get guard condition source
2201+
SourceRange range = CondList[0].getSourceRange();
2202+
SourceManager &SM = RangeInfo.RangeContext->getASTContext().SourceMgr;
2203+
auto CondCharRange = Lexer::getCharSourceRangeFromSourceRange(SM, range);
2204+
2205+
llvm::SmallString<64> DeclBuffer;
2206+
llvm::raw_svector_ostream OS(DeclBuffer);
2207+
2208+
llvm::StringRef Space = " ";
2209+
llvm::StringRef NewLine = "\n";
2210+
2211+
OS << tok::kw_if << Space;
2212+
OS << CondCharRange.str().str() << Space;
2213+
OS << tok::l_brace << NewLine;
2214+
2215+
// Get nodes after guard to place them at if-let body
2216+
if (RangeInfo.ContainedNodes.size() > 1) {
2217+
auto S = RangeInfo.ContainedNodes[1].getSourceRange();
2218+
S.widen(RangeInfo.ContainedNodes.back().getSourceRange());
2219+
auto BodyCharRange = Lexer::getCharSourceRangeFromSourceRange(SM, S);
2220+
OS << BodyCharRange.str().str() << NewLine;
2221+
}
2222+
OS << tok::r_brace;
2223+
2224+
// Get guard body
2225+
auto Body = dyn_cast_or_null<BraceStmt>(Guard->getBody());
2226+
2227+
if (Body && Body->getElements().size() > 1) {
2228+
auto firstElement = Body->getElements()[0];
2229+
auto lastElement = Body->getElements().back();
2230+
SourceRange bodyRange = firstElement.getSourceRange();
2231+
bodyRange.widen(lastElement.getSourceRange());
2232+
auto BodyCharRange = Lexer::getCharSourceRangeFromSourceRange(SM, bodyRange);
2233+
OS << Space << tok::kw_else << Space << tok::l_brace << NewLine;
2234+
OS << BodyCharRange.str().str() << NewLine;
2235+
OS << tok::r_brace;
2236+
}
2237+
2238+
// Replace guard to if-let
2239+
auto ReplaceRange = RangeInfo.ContentRange;
2240+
EditConsumer.accept(SM, ReplaceRange, DeclBuffer.str());
2241+
2242+
return false;
2243+
}
2244+
20712245
/// Struct containing info about an IfStmt that can be converted into an IfExpr.
20722246
struct ConvertToTernaryExprInfo {
20732247
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+
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
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+
if let idx = idxOpt {
24+
print(idx)
25+
} else {
26+
print("nil")
27+
}
28+
}
29+
30+
// RUN: rm -rf %t.result && mkdir -p %t.result
31+
32+
// RUN: %refactor -convert-to-guard -source-filename %s -pos=2:3 -end-pos=4:4 > %t.result/L2-3.swift
33+
// RUN: diff -u %S/Outputs/basic/L2-3.swift.expected %t.result/L2-3.swift
34+
35+
// RUN: %refactor -convert-to-guard -source-filename %s -pos=8:3 -end-pos=11:4 > %t.result/L8-3.swift
36+
// RUN: diff -u %S/Outputs/basic/L8-3.swift.expected %t.result/L8-3.swift
37+
38+
// RUN: %refactor -convert-to-guard -source-filename %s -pos=15:3 -end-pos=19:4 > %t.result/L15-3.swift
39+
// RUN: diff -u %S/Outputs/basic/L15-3.swift.expected %t.result/L15-3.swift
40+
41+
// RUN: %refactor -convert-to-guard -source-filename %s -pos=23:3 -end-pos=27:4 > %t.result/L23-3.swift
42+
// RUN: diff -u %S/Outputs/basic/L23-3.swift.expected %t.result/L23-3.swift

0 commit comments

Comments
 (0)