xref: /freebsd/contrib/llvm-project/clang/lib/ARCMigrate/TransProtectedScope.cpp (revision 0b57cec536236d46e3dba9bd041533462f33dbb7)
1*0b57cec5SDimitry Andric //===--- TransProtectedScope.cpp - Transformations to ARC mode ------------===//
2*0b57cec5SDimitry Andric //
3*0b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*0b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*0b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*0b57cec5SDimitry Andric //
7*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
8*0b57cec5SDimitry Andric //
9*0b57cec5SDimitry Andric // Adds brackets in case statements that "contain" initialization of retaining
10*0b57cec5SDimitry Andric // variable, thus emitting the "switch case is in protected scope" error.
11*0b57cec5SDimitry Andric //
12*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
13*0b57cec5SDimitry Andric 
14*0b57cec5SDimitry Andric #include "Transforms.h"
15*0b57cec5SDimitry Andric #include "Internals.h"
16*0b57cec5SDimitry Andric #include "clang/AST/ASTContext.h"
17*0b57cec5SDimitry Andric #include "clang/Sema/SemaDiagnostic.h"
18*0b57cec5SDimitry Andric 
19*0b57cec5SDimitry Andric using namespace clang;
20*0b57cec5SDimitry Andric using namespace arcmt;
21*0b57cec5SDimitry Andric using namespace trans;
22*0b57cec5SDimitry Andric 
23*0b57cec5SDimitry Andric namespace {
24*0b57cec5SDimitry Andric 
25*0b57cec5SDimitry Andric class LocalRefsCollector : public RecursiveASTVisitor<LocalRefsCollector> {
26*0b57cec5SDimitry Andric   SmallVectorImpl<DeclRefExpr *> &Refs;
27*0b57cec5SDimitry Andric 
28*0b57cec5SDimitry Andric public:
29*0b57cec5SDimitry Andric   LocalRefsCollector(SmallVectorImpl<DeclRefExpr *> &refs)
30*0b57cec5SDimitry Andric     : Refs(refs) { }
31*0b57cec5SDimitry Andric 
32*0b57cec5SDimitry Andric   bool VisitDeclRefExpr(DeclRefExpr *E) {
33*0b57cec5SDimitry Andric     if (ValueDecl *D = E->getDecl())
34*0b57cec5SDimitry Andric       if (D->getDeclContext()->getRedeclContext()->isFunctionOrMethod())
35*0b57cec5SDimitry Andric         Refs.push_back(E);
36*0b57cec5SDimitry Andric     return true;
37*0b57cec5SDimitry Andric   }
38*0b57cec5SDimitry Andric };
39*0b57cec5SDimitry Andric 
40*0b57cec5SDimitry Andric struct CaseInfo {
41*0b57cec5SDimitry Andric   SwitchCase *SC;
42*0b57cec5SDimitry Andric   SourceRange Range;
43*0b57cec5SDimitry Andric   enum {
44*0b57cec5SDimitry Andric     St_Unchecked,
45*0b57cec5SDimitry Andric     St_CannotFix,
46*0b57cec5SDimitry Andric     St_Fixed
47*0b57cec5SDimitry Andric   } State;
48*0b57cec5SDimitry Andric 
49*0b57cec5SDimitry Andric   CaseInfo() : SC(nullptr), State(St_Unchecked) {}
50*0b57cec5SDimitry Andric   CaseInfo(SwitchCase *S, SourceRange Range)
51*0b57cec5SDimitry Andric     : SC(S), Range(Range), State(St_Unchecked) {}
52*0b57cec5SDimitry Andric };
53*0b57cec5SDimitry Andric 
54*0b57cec5SDimitry Andric class CaseCollector : public RecursiveASTVisitor<CaseCollector> {
55*0b57cec5SDimitry Andric   ParentMap &PMap;
56*0b57cec5SDimitry Andric   SmallVectorImpl<CaseInfo> &Cases;
57*0b57cec5SDimitry Andric 
58*0b57cec5SDimitry Andric public:
59*0b57cec5SDimitry Andric   CaseCollector(ParentMap &PMap, SmallVectorImpl<CaseInfo> &Cases)
60*0b57cec5SDimitry Andric     : PMap(PMap), Cases(Cases) { }
61*0b57cec5SDimitry Andric 
62*0b57cec5SDimitry Andric   bool VisitSwitchStmt(SwitchStmt *S) {
63*0b57cec5SDimitry Andric     SwitchCase *Curr = S->getSwitchCaseList();
64*0b57cec5SDimitry Andric     if (!Curr)
65*0b57cec5SDimitry Andric       return true;
66*0b57cec5SDimitry Andric     Stmt *Parent = getCaseParent(Curr);
67*0b57cec5SDimitry Andric     Curr = Curr->getNextSwitchCase();
68*0b57cec5SDimitry Andric     // Make sure all case statements are in the same scope.
69*0b57cec5SDimitry Andric     while (Curr) {
70*0b57cec5SDimitry Andric       if (getCaseParent(Curr) != Parent)
71*0b57cec5SDimitry Andric         return true;
72*0b57cec5SDimitry Andric       Curr = Curr->getNextSwitchCase();
73*0b57cec5SDimitry Andric     }
74*0b57cec5SDimitry Andric 
75*0b57cec5SDimitry Andric     SourceLocation NextLoc = S->getEndLoc();
76*0b57cec5SDimitry Andric     Curr = S->getSwitchCaseList();
77*0b57cec5SDimitry Andric     // We iterate over case statements in reverse source-order.
78*0b57cec5SDimitry Andric     while (Curr) {
79*0b57cec5SDimitry Andric       Cases.push_back(
80*0b57cec5SDimitry Andric           CaseInfo(Curr, SourceRange(Curr->getBeginLoc(), NextLoc)));
81*0b57cec5SDimitry Andric       NextLoc = Curr->getBeginLoc();
82*0b57cec5SDimitry Andric       Curr = Curr->getNextSwitchCase();
83*0b57cec5SDimitry Andric     }
84*0b57cec5SDimitry Andric     return true;
85*0b57cec5SDimitry Andric   }
86*0b57cec5SDimitry Andric 
87*0b57cec5SDimitry Andric   Stmt *getCaseParent(SwitchCase *S) {
88*0b57cec5SDimitry Andric     Stmt *Parent = PMap.getParent(S);
89*0b57cec5SDimitry Andric     while (Parent && (isa<SwitchCase>(Parent) || isa<LabelStmt>(Parent)))
90*0b57cec5SDimitry Andric       Parent = PMap.getParent(Parent);
91*0b57cec5SDimitry Andric     return Parent;
92*0b57cec5SDimitry Andric   }
93*0b57cec5SDimitry Andric };
94*0b57cec5SDimitry Andric 
95*0b57cec5SDimitry Andric class ProtectedScopeFixer {
96*0b57cec5SDimitry Andric   MigrationPass &Pass;
97*0b57cec5SDimitry Andric   SourceManager &SM;
98*0b57cec5SDimitry Andric   SmallVector<CaseInfo, 16> Cases;
99*0b57cec5SDimitry Andric   SmallVector<DeclRefExpr *, 16> LocalRefs;
100*0b57cec5SDimitry Andric 
101*0b57cec5SDimitry Andric public:
102*0b57cec5SDimitry Andric   ProtectedScopeFixer(BodyContext &BodyCtx)
103*0b57cec5SDimitry Andric     : Pass(BodyCtx.getMigrationContext().Pass),
104*0b57cec5SDimitry Andric       SM(Pass.Ctx.getSourceManager()) {
105*0b57cec5SDimitry Andric 
106*0b57cec5SDimitry Andric     CaseCollector(BodyCtx.getParentMap(), Cases)
107*0b57cec5SDimitry Andric         .TraverseStmt(BodyCtx.getTopStmt());
108*0b57cec5SDimitry Andric     LocalRefsCollector(LocalRefs).TraverseStmt(BodyCtx.getTopStmt());
109*0b57cec5SDimitry Andric 
110*0b57cec5SDimitry Andric     SourceRange BodyRange = BodyCtx.getTopStmt()->getSourceRange();
111*0b57cec5SDimitry Andric     const CapturedDiagList &DiagList = Pass.getDiags();
112*0b57cec5SDimitry Andric     // Copy the diagnostics so we don't have to worry about invaliding iterators
113*0b57cec5SDimitry Andric     // from the diagnostic list.
114*0b57cec5SDimitry Andric     SmallVector<StoredDiagnostic, 16> StoredDiags;
115*0b57cec5SDimitry Andric     StoredDiags.append(DiagList.begin(), DiagList.end());
116*0b57cec5SDimitry Andric     SmallVectorImpl<StoredDiagnostic>::iterator
117*0b57cec5SDimitry Andric         I = StoredDiags.begin(), E = StoredDiags.end();
118*0b57cec5SDimitry Andric     while (I != E) {
119*0b57cec5SDimitry Andric       if (I->getID() == diag::err_switch_into_protected_scope &&
120*0b57cec5SDimitry Andric           isInRange(I->getLocation(), BodyRange)) {
121*0b57cec5SDimitry Andric         handleProtectedScopeError(I, E);
122*0b57cec5SDimitry Andric         continue;
123*0b57cec5SDimitry Andric       }
124*0b57cec5SDimitry Andric       ++I;
125*0b57cec5SDimitry Andric     }
126*0b57cec5SDimitry Andric   }
127*0b57cec5SDimitry Andric 
128*0b57cec5SDimitry Andric   void handleProtectedScopeError(
129*0b57cec5SDimitry Andric                              SmallVectorImpl<StoredDiagnostic>::iterator &DiagI,
130*0b57cec5SDimitry Andric                              SmallVectorImpl<StoredDiagnostic>::iterator DiagE){
131*0b57cec5SDimitry Andric     Transaction Trans(Pass.TA);
132*0b57cec5SDimitry Andric     assert(DiagI->getID() == diag::err_switch_into_protected_scope);
133*0b57cec5SDimitry Andric     SourceLocation ErrLoc = DiagI->getLocation();
134*0b57cec5SDimitry Andric     bool handledAllNotes = true;
135*0b57cec5SDimitry Andric     ++DiagI;
136*0b57cec5SDimitry Andric     for (; DiagI != DiagE && DiagI->getLevel() == DiagnosticsEngine::Note;
137*0b57cec5SDimitry Andric          ++DiagI) {
138*0b57cec5SDimitry Andric       if (!handleProtectedNote(*DiagI))
139*0b57cec5SDimitry Andric         handledAllNotes = false;
140*0b57cec5SDimitry Andric     }
141*0b57cec5SDimitry Andric 
142*0b57cec5SDimitry Andric     if (handledAllNotes)
143*0b57cec5SDimitry Andric       Pass.TA.clearDiagnostic(diag::err_switch_into_protected_scope, ErrLoc);
144*0b57cec5SDimitry Andric   }
145*0b57cec5SDimitry Andric 
146*0b57cec5SDimitry Andric   bool handleProtectedNote(const StoredDiagnostic &Diag) {
147*0b57cec5SDimitry Andric     assert(Diag.getLevel() == DiagnosticsEngine::Note);
148*0b57cec5SDimitry Andric 
149*0b57cec5SDimitry Andric     for (unsigned i = 0; i != Cases.size(); i++) {
150*0b57cec5SDimitry Andric       CaseInfo &info = Cases[i];
151*0b57cec5SDimitry Andric       if (isInRange(Diag.getLocation(), info.Range)) {
152*0b57cec5SDimitry Andric 
153*0b57cec5SDimitry Andric         if (info.State == CaseInfo::St_Unchecked)
154*0b57cec5SDimitry Andric           tryFixing(info);
155*0b57cec5SDimitry Andric         assert(info.State != CaseInfo::St_Unchecked);
156*0b57cec5SDimitry Andric 
157*0b57cec5SDimitry Andric         if (info.State == CaseInfo::St_Fixed) {
158*0b57cec5SDimitry Andric           Pass.TA.clearDiagnostic(Diag.getID(), Diag.getLocation());
159*0b57cec5SDimitry Andric           return true;
160*0b57cec5SDimitry Andric         }
161*0b57cec5SDimitry Andric         return false;
162*0b57cec5SDimitry Andric       }
163*0b57cec5SDimitry Andric     }
164*0b57cec5SDimitry Andric 
165*0b57cec5SDimitry Andric     return false;
166*0b57cec5SDimitry Andric   }
167*0b57cec5SDimitry Andric 
168*0b57cec5SDimitry Andric   void tryFixing(CaseInfo &info) {
169*0b57cec5SDimitry Andric     assert(info.State == CaseInfo::St_Unchecked);
170*0b57cec5SDimitry Andric     if (hasVarReferencedOutside(info)) {
171*0b57cec5SDimitry Andric       info.State = CaseInfo::St_CannotFix;
172*0b57cec5SDimitry Andric       return;
173*0b57cec5SDimitry Andric     }
174*0b57cec5SDimitry Andric 
175*0b57cec5SDimitry Andric     Pass.TA.insertAfterToken(info.SC->getColonLoc(), " {");
176*0b57cec5SDimitry Andric     Pass.TA.insert(info.Range.getEnd(), "}\n");
177*0b57cec5SDimitry Andric     info.State = CaseInfo::St_Fixed;
178*0b57cec5SDimitry Andric   }
179*0b57cec5SDimitry Andric 
180*0b57cec5SDimitry Andric   bool hasVarReferencedOutside(CaseInfo &info) {
181*0b57cec5SDimitry Andric     for (unsigned i = 0, e = LocalRefs.size(); i != e; ++i) {
182*0b57cec5SDimitry Andric       DeclRefExpr *DRE = LocalRefs[i];
183*0b57cec5SDimitry Andric       if (isInRange(DRE->getDecl()->getLocation(), info.Range) &&
184*0b57cec5SDimitry Andric           !isInRange(DRE->getLocation(), info.Range))
185*0b57cec5SDimitry Andric         return true;
186*0b57cec5SDimitry Andric     }
187*0b57cec5SDimitry Andric     return false;
188*0b57cec5SDimitry Andric   }
189*0b57cec5SDimitry Andric 
190*0b57cec5SDimitry Andric   bool isInRange(SourceLocation Loc, SourceRange R) {
191*0b57cec5SDimitry Andric     if (Loc.isInvalid())
192*0b57cec5SDimitry Andric       return false;
193*0b57cec5SDimitry Andric     return !SM.isBeforeInTranslationUnit(Loc, R.getBegin()) &&
194*0b57cec5SDimitry Andric             SM.isBeforeInTranslationUnit(Loc, R.getEnd());
195*0b57cec5SDimitry Andric   }
196*0b57cec5SDimitry Andric };
197*0b57cec5SDimitry Andric 
198*0b57cec5SDimitry Andric } // anonymous namespace
199*0b57cec5SDimitry Andric 
200*0b57cec5SDimitry Andric void ProtectedScopeTraverser::traverseBody(BodyContext &BodyCtx) {
201*0b57cec5SDimitry Andric   ProtectedScopeFixer Fix(BodyCtx);
202*0b57cec5SDimitry Andric }
203