1313#include " swift/SILOptimizer/PassManager/Passes.h"
1414#include " swift/SILOptimizer/PassManager/Transforms.h"
1515#include " swift/AST/ASTContext.h"
16+ #include " swift/AST/ASTPrinter.h"
1617#include " swift/AST/DiagnosticEngine.h"
1718#include " swift/AST/DiagnosticsSIL.h"
1819#include " swift/AST/Expr.h"
2122#include " swift/SIL/SILLocation.h"
2223#include " swift/SIL/SILModule.h"
2324#include " swift/SIL/SILVisitor.h"
25+ #include " swift/Syntax/TokenKinds.h"
26+
2427using namespace swift ;
2528
2629template <typename ...T, typename ...U>
@@ -57,6 +60,88 @@ static void diagnoseMissingReturn(const UnreachableInst *UI,
5760}
5861
5962
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+
60145static void diagnoseUnreachable (const SILInstruction *I,
61146 ASTContext &Context) {
62147 if (auto *UI = dyn_cast<UnreachableInst>(I)) {
@@ -83,7 +168,7 @@ static void diagnoseUnreachable(const SILInstruction *I,
83168
84169 // A non-exhaustive switch would also produce an unreachable instruction.
85170 if (L.isASTNode <SwitchStmt>()) {
86- diagnose (Context, L.getEndSourceLoc (), diag::non_exhaustive_switch );
171+ diagnoseMissingCases (Context, L.getAsASTNode <SwitchStmt>() );
87172 return ;
88173 }
89174
0 commit comments