Skip to content

Commit 94ab95e

Browse files
committed
[Refactoring] Move ExpandSwitchCases and ExpandDefault to their own file
1 parent ef4ee95 commit 94ab95e

File tree

4 files changed

+160
-146
lines changed

4 files changed

+160
-146
lines changed

lib/Refactoring/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ add_swift_host_library(swiftRefactoring STATIC
55
ConvertIfLetExprToGuardExpr.cpp
66
ConvertToSwitchStmt.cpp
77
ConvertToTernaryExpr.cpp
8+
ExpandSwitchCases.cpp
89
ExpandTernaryExpr.cpp
910
ExtractExpr.cpp
1011
ExtractExprBase.cpp

lib/Refactoring/ContextFinder.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "swift/AST/ASTContext.h"
1414
#include "swift/AST/SourceFile.h"
1515
#include "swift/Basic/SourceManager.h"
16+
#include "swift/IDE/SourceEntityWalker.h"
1617

1718
namespace swift {
1819
namespace refactoring {

lib/Refactoring/ExpandSwitchCases.cpp

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
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/DiagnosticsRefactoring.h"
16+
#include "swift/AST/Pattern.h"
17+
#include "swift/AST/Stmt.h"
18+
19+
using namespace swift::refactoring;
20+
21+
static EnumDecl *getEnumDeclFromSwitchStmt(SwitchStmt *SwitchS) {
22+
if (auto SubjectTy = SwitchS->getSubjectExpr()->getType()) {
23+
// FIXME: Support more complex subject like '(Enum1, Enum2)'.
24+
return dyn_cast_or_null<EnumDecl>(SubjectTy->getAnyNominal());
25+
}
26+
return nullptr;
27+
}
28+
29+
static bool performCasesExpansionInSwitchStmt(SwitchStmt *SwitchS,
30+
DiagnosticEngine &DiagEngine,
31+
SourceLoc ExpandedStmtLoc,
32+
EditorConsumerInsertStream &OS) {
33+
// Assume enum elements are not handled in the switch statement.
34+
auto EnumDecl = getEnumDeclFromSwitchStmt(SwitchS);
35+
assert(EnumDecl);
36+
llvm::DenseSet<EnumElementDecl *> UnhandledElements;
37+
EnumDecl->getAllElements(UnhandledElements);
38+
for (auto Current : SwitchS->getCases()) {
39+
if (Current->isDefault()) {
40+
continue;
41+
}
42+
// For each handled enum element, remove it from the bucket.
43+
for (auto Item : Current->getCaseLabelItems()) {
44+
if (auto *EEP = dyn_cast_or_null<EnumElementPattern>(Item.getPattern())) {
45+
UnhandledElements.erase(EEP->getElementDecl());
46+
}
47+
}
48+
}
49+
50+
// If all enum elements are handled in the switch statement, issue error.
51+
if (UnhandledElements.empty()) {
52+
DiagEngine.diagnose(ExpandedStmtLoc, diag::no_remaining_cases);
53+
return true;
54+
}
55+
56+
printEnumElementsAsCases(UnhandledElements, OS);
57+
return false;
58+
}
59+
60+
// Finds SwitchStmt that contains given CaseStmt.
61+
static SwitchStmt *findEnclosingSwitchStmt(CaseStmt *CS, SourceFile *SF,
62+
DiagnosticEngine &DiagEngine) {
63+
auto IsSwitch = [](ASTNode Node) {
64+
return Node.is<Stmt *>() &&
65+
Node.get<Stmt *>()->getKind() == StmtKind::Switch;
66+
};
67+
ContextFinder Finder(*SF, CS, IsSwitch);
68+
Finder.resolve();
69+
70+
// If failed to find the switch statement, issue error.
71+
if (Finder.getContexts().empty()) {
72+
DiagEngine.diagnose(CS->getStartLoc(), diag::no_parent_switch);
73+
return nullptr;
74+
}
75+
auto *SwitchS =
76+
static_cast<SwitchStmt *>(Finder.getContexts().back().get<Stmt *>());
77+
// Make sure that CaseStmt is included in switch that was found.
78+
auto Cases = SwitchS->getCases();
79+
auto Default = std::find(Cases.begin(), Cases.end(), CS);
80+
if (Default == Cases.end()) {
81+
DiagEngine.diagnose(CS->getStartLoc(), diag::no_parent_switch);
82+
return nullptr;
83+
}
84+
return SwitchS;
85+
}
86+
87+
bool RefactoringActionExpandDefault::isApplicable(
88+
ResolvedCursorInfoPtr CursorInfo, DiagnosticEngine &Diag) {
89+
auto Exit = [&](bool Applicable) {
90+
if (!Applicable)
91+
Diag.diagnose(SourceLoc(), diag::invalid_default_location);
92+
return Applicable;
93+
};
94+
auto StmtStartInfo = dyn_cast<ResolvedStmtStartCursorInfo>(CursorInfo);
95+
if (!StmtStartInfo)
96+
return Exit(false);
97+
if (auto *CS = dyn_cast<CaseStmt>(StmtStartInfo->getTrailingStmt())) {
98+
auto EnclosingSwitchStmt =
99+
findEnclosingSwitchStmt(CS, CursorInfo->getSourceFile(), Diag);
100+
if (!EnclosingSwitchStmt)
101+
return false;
102+
auto EnumD = getEnumDeclFromSwitchStmt(EnclosingSwitchStmt);
103+
auto IsApplicable = CS->isDefault() && EnumD != nullptr;
104+
return IsApplicable;
105+
}
106+
return Exit(false);
107+
}
108+
109+
bool RefactoringActionExpandDefault::performChange() {
110+
// If we've not seen the default statement inside the switch statement, issue
111+
// error.
112+
auto StmtStartInfo = cast<ResolvedStmtStartCursorInfo>(CursorInfo);
113+
auto *CS = static_cast<CaseStmt *>(StmtStartInfo->getTrailingStmt());
114+
auto *SwitchS = findEnclosingSwitchStmt(CS, TheFile, DiagEngine);
115+
assert(SwitchS);
116+
EditorConsumerInsertStream OS(
117+
EditConsumer, SM,
118+
Lexer::getCharSourceRangeFromSourceRange(SM, CS->getLabelItemsRange()));
119+
return performCasesExpansionInSwitchStmt(SwitchS, DiagEngine,
120+
CS->getStartLoc(), OS);
121+
}
122+
123+
bool RefactoringActionExpandSwitchCases::isApplicable(
124+
ResolvedCursorInfoPtr CursorInfo, DiagnosticEngine &DiagEngine) {
125+
auto StmtStartInfo = dyn_cast<ResolvedStmtStartCursorInfo>(CursorInfo);
126+
if (!StmtStartInfo || !StmtStartInfo->getTrailingStmt())
127+
return false;
128+
if (auto *Switch = dyn_cast<SwitchStmt>(StmtStartInfo->getTrailingStmt())) {
129+
return getEnumDeclFromSwitchStmt(Switch);
130+
}
131+
return false;
132+
}
133+
134+
bool RefactoringActionExpandSwitchCases::performChange() {
135+
auto StmtStartInfo = cast<ResolvedStmtStartCursorInfo>(CursorInfo);
136+
auto *SwitchS = dyn_cast<SwitchStmt>(StmtStartInfo->getTrailingStmt());
137+
assert(SwitchS);
138+
139+
auto InsertRange = CharSourceRange();
140+
auto Cases = SwitchS->getCases();
141+
auto Default = std::find_if(Cases.begin(), Cases.end(),
142+
[](CaseStmt *Stmt) { return Stmt->isDefault(); });
143+
if (Default != Cases.end()) {
144+
auto DefaultRange = (*Default)->getLabelItemsRange();
145+
InsertRange = Lexer::getCharSourceRangeFromSourceRange(SM, DefaultRange);
146+
} else {
147+
auto RBraceLoc = SwitchS->getRBraceLoc();
148+
InsertRange = CharSourceRange(SM, RBraceLoc, RBraceLoc);
149+
}
150+
EditorConsumerInsertStream OS(EditConsumer, SM, InsertRange);
151+
if (SM.getLineAndColumnInBuffer(SwitchS->getLBraceLoc()).first ==
152+
SM.getLineAndColumnInBuffer(SwitchS->getRBraceLoc()).first) {
153+
OS << "\n";
154+
}
155+
auto Result = performCasesExpansionInSwitchStmt(SwitchS, DiagEngine,
156+
SwitchS->getStartLoc(), OS);
157+
return Result;
158+
}

lib/Refactoring/Refactoring.cpp

Lines changed: 0 additions & 146 deletions
Original file line numberDiff line numberDiff line change
@@ -566,152 +566,6 @@ collectRefactoringsAtCursor(SourceFile *SF, unsigned Line, unsigned Column,
566566
return collectRefactorings(Tok, /*ExcludeRename=*/false);
567567
}
568568

569-
static EnumDecl* getEnumDeclFromSwitchStmt(SwitchStmt *SwitchS) {
570-
if (auto SubjectTy = SwitchS->getSubjectExpr()->getType()) {
571-
// FIXME: Support more complex subject like '(Enum1, Enum2)'.
572-
return dyn_cast_or_null<EnumDecl>(SubjectTy->getAnyNominal());
573-
}
574-
return nullptr;
575-
}
576-
577-
static bool performCasesExpansionInSwitchStmt(SwitchStmt *SwitchS,
578-
DiagnosticEngine &DiagEngine,
579-
SourceLoc ExpandedStmtLoc,
580-
EditorConsumerInsertStream &OS
581-
) {
582-
// Assume enum elements are not handled in the switch statement.
583-
auto EnumDecl = getEnumDeclFromSwitchStmt(SwitchS);
584-
assert(EnumDecl);
585-
llvm::DenseSet<EnumElementDecl*> UnhandledElements;
586-
EnumDecl->getAllElements(UnhandledElements);
587-
for (auto Current : SwitchS->getCases()) {
588-
if (Current->isDefault()) {
589-
continue;
590-
}
591-
// For each handled enum element, remove it from the bucket.
592-
for (auto Item : Current->getCaseLabelItems()) {
593-
if (auto *EEP = dyn_cast_or_null<EnumElementPattern>(Item.getPattern())) {
594-
UnhandledElements.erase(EEP->getElementDecl());
595-
}
596-
}
597-
}
598-
599-
// If all enum elements are handled in the switch statement, issue error.
600-
if (UnhandledElements.empty()) {
601-
DiagEngine.diagnose(ExpandedStmtLoc, diag::no_remaining_cases);
602-
return true;
603-
}
604-
605-
printEnumElementsAsCases(UnhandledElements, OS);
606-
return false;
607-
}
608-
609-
// Finds SwitchStmt that contains given CaseStmt.
610-
static SwitchStmt* findEnclosingSwitchStmt(CaseStmt *CS,
611-
SourceFile *SF,
612-
DiagnosticEngine &DiagEngine) {
613-
auto IsSwitch = [](ASTNode Node) {
614-
return Node.is<Stmt*>() &&
615-
Node.get<Stmt*>()->getKind() == StmtKind::Switch;
616-
};
617-
ContextFinder Finder(*SF, CS, IsSwitch);
618-
Finder.resolve();
619-
620-
// If failed to find the switch statement, issue error.
621-
if (Finder.getContexts().empty()) {
622-
DiagEngine.diagnose(CS->getStartLoc(), diag::no_parent_switch);
623-
return nullptr;
624-
}
625-
auto *SwitchS = static_cast<SwitchStmt*>(Finder.getContexts().back().
626-
get<Stmt*>());
627-
// Make sure that CaseStmt is included in switch that was found.
628-
auto Cases = SwitchS->getCases();
629-
auto Default = std::find(Cases.begin(), Cases.end(), CS);
630-
if (Default == Cases.end()) {
631-
DiagEngine.diagnose(CS->getStartLoc(), diag::no_parent_switch);
632-
return nullptr;
633-
}
634-
return SwitchS;
635-
}
636-
637-
bool RefactoringActionExpandDefault::isApplicable(
638-
ResolvedCursorInfoPtr CursorInfo, DiagnosticEngine &Diag) {
639-
auto Exit = [&](bool Applicable) {
640-
if (!Applicable)
641-
Diag.diagnose(SourceLoc(), diag::invalid_default_location);
642-
return Applicable;
643-
};
644-
auto StmtStartInfo = dyn_cast<ResolvedStmtStartCursorInfo>(CursorInfo);
645-
if (!StmtStartInfo)
646-
return Exit(false);
647-
if (auto *CS = dyn_cast<CaseStmt>(StmtStartInfo->getTrailingStmt())) {
648-
auto EnclosingSwitchStmt =
649-
findEnclosingSwitchStmt(CS, CursorInfo->getSourceFile(), Diag);
650-
if (!EnclosingSwitchStmt)
651-
return false;
652-
auto EnumD = getEnumDeclFromSwitchStmt(EnclosingSwitchStmt);
653-
auto IsApplicable = CS->isDefault() && EnumD != nullptr;
654-
return IsApplicable;
655-
}
656-
return Exit(false);
657-
}
658-
659-
bool RefactoringActionExpandDefault::performChange() {
660-
// If we've not seen the default statement inside the switch statement, issue
661-
// error.
662-
auto StmtStartInfo = cast<ResolvedStmtStartCursorInfo>(CursorInfo);
663-
auto *CS = static_cast<CaseStmt *>(StmtStartInfo->getTrailingStmt());
664-
auto *SwitchS = findEnclosingSwitchStmt(CS, TheFile, DiagEngine);
665-
assert(SwitchS);
666-
EditorConsumerInsertStream OS(EditConsumer, SM,
667-
Lexer::getCharSourceRangeFromSourceRange(SM,
668-
CS->getLabelItemsRange()));
669-
return performCasesExpansionInSwitchStmt(SwitchS,
670-
DiagEngine,
671-
CS->getStartLoc(),
672-
OS);
673-
}
674-
675-
bool RefactoringActionExpandSwitchCases::isApplicable(
676-
ResolvedCursorInfoPtr CursorInfo, DiagnosticEngine &DiagEngine) {
677-
auto StmtStartInfo = dyn_cast<ResolvedStmtStartCursorInfo>(CursorInfo);
678-
if (!StmtStartInfo || !StmtStartInfo->getTrailingStmt())
679-
return false;
680-
if (auto *Switch = dyn_cast<SwitchStmt>(StmtStartInfo->getTrailingStmt())) {
681-
return getEnumDeclFromSwitchStmt(Switch);
682-
}
683-
return false;
684-
}
685-
686-
bool RefactoringActionExpandSwitchCases::performChange() {
687-
auto StmtStartInfo = cast<ResolvedStmtStartCursorInfo>(CursorInfo);
688-
auto *SwitchS = dyn_cast<SwitchStmt>(StmtStartInfo->getTrailingStmt());
689-
assert(SwitchS);
690-
691-
auto InsertRange = CharSourceRange();
692-
auto Cases = SwitchS->getCases();
693-
auto Default = std::find_if(Cases.begin(), Cases.end(), [](CaseStmt *Stmt) {
694-
return Stmt->isDefault();
695-
});
696-
if (Default != Cases.end()) {
697-
auto DefaultRange = (*Default)->getLabelItemsRange();
698-
InsertRange = Lexer::getCharSourceRangeFromSourceRange(SM, DefaultRange);
699-
} else {
700-
auto RBraceLoc = SwitchS->getRBraceLoc();
701-
InsertRange = CharSourceRange(SM, RBraceLoc, RBraceLoc);
702-
}
703-
EditorConsumerInsertStream OS(EditConsumer, SM, InsertRange);
704-
if (SM.getLineAndColumnInBuffer(SwitchS->getLBraceLoc()).first ==
705-
SM.getLineAndColumnInBuffer(SwitchS->getRBraceLoc()).first) {
706-
OS << "\n";
707-
}
708-
auto Result = performCasesExpansionInSwitchStmt(SwitchS,
709-
DiagEngine,
710-
SwitchS->getStartLoc(),
711-
OS);
712-
return Result;
713-
}
714-
715569
static Expr *findLocalizeTarget(ResolvedCursorInfoPtr CursorInfo) {
716570
auto ExprStartInfo = dyn_cast<ResolvedExprStartCursorInfo>(CursorInfo);
717571
if (!ExprStartInfo)

0 commit comments

Comments
 (0)