13
13
#include " swift/SILOptimizer/PassManager/Passes.h"
14
14
#include " swift/SILOptimizer/PassManager/Transforms.h"
15
15
#include " swift/AST/ASTContext.h"
16
+ #include " swift/AST/ASTPrinter.h"
16
17
#include " swift/AST/DiagnosticEngine.h"
17
18
#include " swift/AST/DiagnosticsSIL.h"
18
19
#include " swift/AST/Expr.h"
21
22
#include " swift/SIL/SILLocation.h"
22
23
#include " swift/SIL/SILModule.h"
23
24
#include " swift/SIL/SILVisitor.h"
25
+ #include " swift/Syntax/TokenKinds.h"
26
+
24
27
using namespace swift ;
25
28
26
29
template <typename ...T, typename ...U>
@@ -57,6 +60,88 @@ static void diagnoseMissingReturn(const UnreachableInst *UI,
57
60
}
58
61
59
62
63
+ // / If the pattern handles an enum element, remove the enum element from the
64
+ // / given set. If seeing uncertain patterns, e.g. any pattern, return true;
65
+ // / otherwise return false.
66
+ static bool
67
+ removedHandledEnumElements (Pattern *P,
68
+ llvm::DenseSet<EnumElementDecl*> &UnhandledElements) {
69
+ if (auto *EEP = dyn_cast<EnumElementPattern>(P)) {
70
+ UnhandledElements.erase (EEP->getElementDecl ());
71
+ } else if (auto *VP = dyn_cast<VarPattern>(P)) {
72
+ return removedHandledEnumElements (VP->getSubPattern (), UnhandledElements);
73
+ } else {
74
+ return true ;
75
+ }
76
+ return false ;
77
+ };
78
+
79
+ static void diagnoseMissingCases (ASTContext &Context,
80
+ const SwitchStmt *SwitchS) {
81
+ SourceLoc EndLoc = SwitchS->getEndLoc ();
82
+ StringRef Placeholder = " <#Code#>" ;
83
+ SmallString<32 > Buffer;
84
+ llvm::raw_svector_ostream OS (Buffer);
85
+
86
+ auto DefaultDiag = [&]() {
87
+ OS << tok::kw_default << " : " << Placeholder << " \n " ;
88
+ Context.Diags .diagnose (EndLoc, diag::non_exhaustive_switch).
89
+ fixItInsert (EndLoc, Buffer.str ());
90
+ };
91
+ // To find the subject enum decl for this switch statement.
92
+ EnumDecl *SubjectED = nullptr ;
93
+ if (auto SubjectTy = SwitchS->getSubjectExpr ()->getType ()) {
94
+ if (auto *ND = SubjectTy->getAnyNominal ()) {
95
+ SubjectED = ND->getAsEnumOrEnumExtensionContext ();
96
+ }
97
+ }
98
+
99
+ // The switch is not about an enum decl, add "default:" instead.
100
+ if (!SubjectED) {
101
+ DefaultDiag ();
102
+ return ;
103
+ }
104
+
105
+ // Assume enum elements are not handled in the switch statement.
106
+ llvm::DenseSet<EnumElementDecl*> UnhandledElements;
107
+
108
+ SubjectED->getAllElements (UnhandledElements);
109
+ for (auto Current : SwitchS->getCases ()) {
110
+ // For each handled enum element, remove it from the bucket.
111
+ for (auto Item : Current->getCaseLabelItems ()) {
112
+ if (removedHandledEnumElements (Item.getPattern (), UnhandledElements)) {
113
+ DefaultDiag ();
114
+ return ;
115
+ }
116
+ }
117
+ }
118
+
119
+ // No unhandled cases, so we are not sure the exact case to add, fall back to
120
+ // default instead.
121
+ if (UnhandledElements.empty ()) {
122
+ DefaultDiag ();
123
+ return ;
124
+ }
125
+
126
+ // Sort the missing elements to a vector because set does not guarantee orders.
127
+ SmallVector<EnumElementDecl*, 4 > SortedElements;
128
+ SortedElements.insert (SortedElements.begin (), UnhandledElements.begin (),
129
+ UnhandledElements.end ());
130
+ std::sort (SortedElements.begin (),SortedElements.end (),
131
+ [](EnumElementDecl *LHS, EnumElementDecl *RHS) {
132
+ return LHS->getNameStr ().compare (RHS->getNameStr ()) < 0 ;
133
+ });
134
+
135
+ // Print each enum element name.
136
+ std::for_each (SortedElements.begin (), SortedElements.end (),
137
+ [&](EnumElementDecl *EE) {
138
+ OS << tok::kw_case << " ." << EE->getNameStr () << " : " <<
139
+ Placeholder << " \n " ;
140
+ });
141
+ Context.Diags .diagnose (EndLoc, diag::non_exhaustive_switch).
142
+ fixItInsert (EndLoc, Buffer.str ());
143
+ }
144
+
60
145
static void diagnoseUnreachable (const SILInstruction *I,
61
146
ASTContext &Context) {
62
147
if (auto *UI = dyn_cast<UnreachableInst>(I)) {
@@ -83,7 +168,7 @@ static void diagnoseUnreachable(const SILInstruction *I,
83
168
84
169
// A non-exhaustive switch would also produce an unreachable instruction.
85
170
if (L.isASTNode <SwitchStmt>()) {
86
- diagnose (Context, L.getEndSourceLoc (), diag::non_exhaustive_switch );
171
+ diagnoseMissingCases (Context, L.getAsASTNode <SwitchStmt>() );
87
172
return ;
88
173
}
89
174
0 commit comments