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