blob: b8b25f25941cec057c765d90300fb408230d92e8 [file] [log] [blame]
Argyrios Kyrtzidis03fbe3e2013-01-04 18:30:08 +00001//===--- TransProtectedScope.cpp - Transformations to ARC mode ------------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// Adds brackets in case statements that "contain" initialization of retaining
11// variable, thus emitting the "switch case is in protected scope" error.
12//
13//===----------------------------------------------------------------------===//
14
15#include "Transforms.h"
16#include "Internals.h"
Argyrios Kyrtzidis08281fa2013-01-08 00:58:25 +000017#include "clang/AST/ASTContext.h"
Chandler Carruth4b417452013-01-19 08:09:44 +000018#include "clang/Sema/SemaDiagnostic.h"
Argyrios Kyrtzidis03fbe3e2013-01-04 18:30:08 +000019
20using namespace clang;
21using namespace arcmt;
22using namespace trans;
23
24namespace {
25
Argyrios Kyrtzidis08281fa2013-01-08 00:58:25 +000026class LocalRefsCollector : public RecursiveASTVisitor<LocalRefsCollector> {
27 SmallVectorImpl<DeclRefExpr *> &Refs;
28
29public:
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
Argyrios Kyrtzidis03fbe3e2013-01-04 18:30:08 +000041struct CaseInfo {
42 SwitchCase *SC;
43 SourceRange Range;
Argyrios Kyrtzidis08281fa2013-01-08 00:58:25 +000044 enum {
45 St_Unchecked,
46 St_CannotFix,
47 St_Fixed
48 } State;
Argyrios Kyrtzidis03fbe3e2013-01-04 18:30:08 +000049
Argyrios Kyrtzidis08281fa2013-01-08 00:58:25 +000050 CaseInfo() : SC(0), State(St_Unchecked) {}
Argyrios Kyrtzidis03fbe3e2013-01-04 18:30:08 +000051 CaseInfo(SwitchCase *S, SourceRange Range)
Argyrios Kyrtzidis08281fa2013-01-08 00:58:25 +000052 : SC(S), Range(Range), State(St_Unchecked) {}
Argyrios Kyrtzidis03fbe3e2013-01-04 18:30:08 +000053};
54
55class CaseCollector : public RecursiveASTVisitor<CaseCollector> {
Argyrios Kyrtzidis08281fa2013-01-08 00:58:25 +000056 ParentMap &PMap;
Dmitri Gribenkof8579502013-01-12 19:30:44 +000057 SmallVectorImpl<CaseInfo> &Cases;
Argyrios Kyrtzidis03fbe3e2013-01-04 18:30:08 +000058
59public:
Dmitri Gribenkof8579502013-01-12 19:30:44 +000060 CaseCollector(ParentMap &PMap, SmallVectorImpl<CaseInfo> &Cases)
Argyrios Kyrtzidis08281fa2013-01-08 00:58:25 +000061 : PMap(PMap), Cases(Cases) { }
Argyrios Kyrtzidis03fbe3e2013-01-04 18:30:08 +000062
63 bool VisitSwitchStmt(SwitchStmt *S) {
Argyrios Kyrtzidis03fbe3e2013-01-04 18:30:08 +000064 SwitchCase *Curr = S->getSwitchCaseList();
Argyrios Kyrtzidis08281fa2013-01-08 00:58:25 +000065 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->getLocEnd();
77 Curr = S->getSwitchCaseList();
Argyrios Kyrtzidis03fbe3e2013-01-04 18:30:08 +000078 // We iterate over case statements in reverse source-order.
79 while (Curr) {
80 Cases.push_back(CaseInfo(Curr,SourceRange(Curr->getLocStart(), NextLoc)));
81 NextLoc = Curr->getLocStart();
82 Curr = Curr->getNextSwitchCase();
83 }
84 return true;
85 }
Argyrios Kyrtzidis08281fa2013-01-08 00:58:25 +000086
87 Stmt *getCaseParent(SwitchCase *S) {
88 Stmt *Parent = PMap.getParent(S);
89 while (Parent && (isa<SwitchCase>(Parent) || isa<LabelStmt>(Parent)))
90 Parent = PMap.getParent(Parent);
91 return Parent;
92 }
93};
94
95class ProtectedScopeFixer {
96 MigrationPass &Pass;
97 SourceManager &SM;
98 SmallVector<CaseInfo, 16> Cases;
99 SmallVector<DeclRefExpr *, 16> LocalRefs;
100
101public:
102 ProtectedScopeFixer(BodyContext &BodyCtx)
103 : Pass(BodyCtx.getMigrationContext().Pass),
104 SM(Pass.Ctx.getSourceManager()) {
105
106 CaseCollector(BodyCtx.getParentMap(), Cases)
107 .TraverseStmt(BodyCtx.getTopStmt());
108 LocalRefsCollector(LocalRefs).TraverseStmt(BodyCtx.getTopStmt());
109
110 SourceRange BodyRange = BodyCtx.getTopStmt()->getSourceRange();
111 const CapturedDiagList &DiagList = Pass.getDiags();
112 CapturedDiagList::iterator I = DiagList.begin(), E = DiagList.end();
113 while (I != E) {
114 if (I->getID() == diag::err_switch_into_protected_scope &&
115 isInRange(I->getLocation(), BodyRange)) {
116 handleProtectedScopeError(I, E);
117 continue;
118 }
119 ++I;
120 }
121 }
122
123 void handleProtectedScopeError(CapturedDiagList::iterator &DiagI,
124 CapturedDiagList::iterator DiagE) {
125 Transaction Trans(Pass.TA);
126 assert(DiagI->getID() == diag::err_switch_into_protected_scope);
127 SourceLocation ErrLoc = DiagI->getLocation();
128 bool handledAllNotes = true;
129 ++DiagI;
130 for (; DiagI != DiagE && DiagI->getLevel() == DiagnosticsEngine::Note;
131 ++DiagI) {
132 if (!handleProtectedNote(*DiagI))
133 handledAllNotes = false;
134 }
135
136 if (handledAllNotes)
137 Pass.TA.clearDiagnostic(diag::err_switch_into_protected_scope, ErrLoc);
138 }
139
140 bool handleProtectedNote(const StoredDiagnostic &Diag) {
141 assert(Diag.getLevel() == DiagnosticsEngine::Note);
142
143 for (unsigned i = 0; i != Cases.size(); i++) {
144 CaseInfo &info = Cases[i];
145 if (isInRange(Diag.getLocation(), info.Range)) {
146
147 if (info.State == CaseInfo::St_Unchecked)
148 tryFixing(info);
149 assert(info.State != CaseInfo::St_Unchecked);
150
151 if (info.State == CaseInfo::St_Fixed) {
152 Pass.TA.clearDiagnostic(Diag.getID(), Diag.getLocation());
153 return true;
154 }
155 return false;
156 }
157 }
158
159 return false;
160 }
161
162 void tryFixing(CaseInfo &info) {
163 assert(info.State == CaseInfo::St_Unchecked);
164 if (hasVarReferencedOutside(info)) {
165 info.State = CaseInfo::St_CannotFix;
166 return;
167 }
168
169 Pass.TA.insertAfterToken(info.SC->getColonLoc(), " {");
170 Pass.TA.insert(info.Range.getEnd(), "}\n");
171 info.State = CaseInfo::St_Fixed;
172 }
173
174 bool hasVarReferencedOutside(CaseInfo &info) {
175 for (unsigned i = 0, e = LocalRefs.size(); i != e; ++i) {
176 DeclRefExpr *DRE = LocalRefs[i];
177 if (isInRange(DRE->getDecl()->getLocation(), info.Range) &&
178 !isInRange(DRE->getLocation(), info.Range))
179 return true;
180 }
181 return false;
182 }
183
184 bool isInRange(SourceLocation Loc, SourceRange R) {
185 if (Loc.isInvalid())
186 return false;
187 return !SM.isBeforeInTranslationUnit(Loc, R.getBegin()) &&
188 SM.isBeforeInTranslationUnit(Loc, R.getEnd());
189 }
Argyrios Kyrtzidis03fbe3e2013-01-04 18:30:08 +0000190};
191
192} // anonymous namespace
193
Argyrios Kyrtzidis03fbe3e2013-01-04 18:30:08 +0000194void ProtectedScopeTraverser::traverseBody(BodyContext &BodyCtx) {
Argyrios Kyrtzidis08281fa2013-01-08 00:58:25 +0000195 ProtectedScopeFixer Fix(BodyCtx);
Argyrios Kyrtzidis03fbe3e2013-01-04 18:30:08 +0000196}