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