/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/ARCMigrate/TransProtectedScope.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | |
14 | | #include "Internals.h" |
15 | | #include "Transforms.h" |
16 | | #include "clang/AST/ASTContext.h" |
17 | | #include "clang/Basic/SourceManager.h" |
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 | 403 | : Refs(refs) { } |
32 | | |
33 | 918 | bool VisitDeclRefExpr(DeclRefExpr *E) { |
34 | 918 | if (ValueDecl *D = E->getDecl()) |
35 | 918 | if (D->getDeclContext()->getRedeclContext()->isFunctionOrMethod()) |
36 | 752 | Refs.push_back(E); |
37 | 918 | return true; |
38 | 918 | } |
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 | 0 | CaseInfo() : SC(nullptr), State(St_Unchecked) {} |
51 | | CaseInfo(SwitchCase *S, SourceRange Range) |
52 | 18 | : 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 | 403 | : PMap(PMap), Cases(Cases) { } |
62 | | |
63 | 6 | bool VisitSwitchStmt(SwitchStmt *S) { |
64 | 6 | SwitchCase *Curr = S->getSwitchCaseList(); |
65 | 6 | if (!Curr) |
66 | 0 | return true; |
67 | 6 | Stmt *Parent = getCaseParent(Curr); |
68 | 6 | Curr = Curr->getNextSwitchCase(); |
69 | | // Make sure all case statements are in the same scope. |
70 | 18 | while (Curr) { |
71 | 12 | if (getCaseParent(Curr) != Parent) |
72 | 0 | return true; |
73 | 12 | Curr = Curr->getNextSwitchCase(); |
74 | 12 | } |
75 | | |
76 | 6 | SourceLocation NextLoc = S->getEndLoc(); |
77 | 6 | Curr = S->getSwitchCaseList(); |
78 | | // We iterate over case statements in reverse source-order. |
79 | 24 | while (Curr) { |
80 | 18 | Cases.push_back( |
81 | 18 | CaseInfo(Curr, SourceRange(Curr->getBeginLoc(), NextLoc))); |
82 | 18 | NextLoc = Curr->getBeginLoc(); |
83 | 18 | Curr = Curr->getNextSwitchCase(); |
84 | 18 | } |
85 | 6 | return true; |
86 | 6 | } |
87 | | |
88 | 18 | Stmt *getCaseParent(SwitchCase *S) { |
89 | 18 | Stmt *Parent = PMap.getParent(S); |
90 | 20 | while (Parent && (isa<SwitchCase>(Parent) || isa<LabelStmt>(Parent)18 )) |
91 | 2 | Parent = PMap.getParent(Parent); |
92 | 18 | return Parent; |
93 | 18 | } |
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 | 403 | SM(Pass.Ctx.getSourceManager()) { |
106 | | |
107 | 403 | CaseCollector(BodyCtx.getParentMap(), Cases) |
108 | 403 | .TraverseStmt(BodyCtx.getTopStmt()); |
109 | 403 | LocalRefsCollector(LocalRefs).TraverseStmt(BodyCtx.getTopStmt()); |
110 | | |
111 | 403 | SourceRange BodyRange = BodyCtx.getTopStmt()->getSourceRange(); |
112 | 403 | 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 | 403 | SmallVector<StoredDiagnostic, 16> StoredDiags; |
116 | 403 | StoredDiags.append(DiagList.begin(), DiagList.end()); |
117 | 403 | SmallVectorImpl<StoredDiagnostic>::iterator |
118 | 403 | I = StoredDiags.begin(), E = StoredDiags.end(); |
119 | 6.19k | while (I != E) { |
120 | 5.79k | if (I->getID() == diag::err_switch_into_protected_scope && |
121 | 5.79k | isInRange(I->getLocation(), BodyRange)141 ) { |
122 | 12 | handleProtectedScopeError(I, E); |
123 | 12 | continue; |
124 | 12 | } |
125 | 5.78k | ++I; |
126 | 5.78k | } |
127 | 403 | } |
128 | | |
129 | | void handleProtectedScopeError( |
130 | | SmallVectorImpl<StoredDiagnostic>::iterator &DiagI, |
131 | 12 | SmallVectorImpl<StoredDiagnostic>::iterator DiagE){ |
132 | 12 | Transaction Trans(Pass.TA); |
133 | 12 | assert(DiagI->getID() == diag::err_switch_into_protected_scope); |
134 | 0 | SourceLocation ErrLoc = DiagI->getLocation(); |
135 | 12 | bool handledAllNotes = true; |
136 | 12 | ++DiagI; |
137 | 38 | for (; DiagI != DiagE && DiagI->getLevel() == DiagnosticsEngine::Note36 ; |
138 | 26 | ++DiagI) { |
139 | 26 | if (!handleProtectedNote(*DiagI)) |
140 | 1 | handledAllNotes = false; |
141 | 26 | } |
142 | | |
143 | 12 | if (handledAllNotes) |
144 | 11 | Pass.TA.clearDiagnostic(diag::err_switch_into_protected_scope, ErrLoc); |
145 | 12 | } |
146 | | |
147 | 26 | bool handleProtectedNote(const StoredDiagnostic &Diag) { |
148 | 26 | assert(Diag.getLevel() == DiagnosticsEngine::Note); |
149 | | |
150 | 112 | for (unsigned i = 0; i != Cases.size(); i++86 ) { |
151 | 112 | CaseInfo &info = Cases[i]; |
152 | 112 | if (isInRange(Diag.getLocation(), info.Range)) { |
153 | | |
154 | 26 | if (info.State == CaseInfo::St_Unchecked) |
155 | 8 | tryFixing(info); |
156 | 26 | assert(info.State != CaseInfo::St_Unchecked); |
157 | | |
158 | 26 | if (info.State == CaseInfo::St_Fixed) { |
159 | 25 | Pass.TA.clearDiagnostic(Diag.getID(), Diag.getLocation()); |
160 | 25 | return true; |
161 | 25 | } |
162 | 1 | return false; |
163 | 26 | } |
164 | 112 | } |
165 | | |
166 | 0 | return false; |
167 | 26 | } |
168 | | |
169 | 8 | void tryFixing(CaseInfo &info) { |
170 | 8 | assert(info.State == CaseInfo::St_Unchecked); |
171 | 8 | if (hasVarReferencedOutside(info)) { |
172 | 1 | info.State = CaseInfo::St_CannotFix; |
173 | 1 | return; |
174 | 1 | } |
175 | | |
176 | 7 | Pass.TA.insertAfterToken(info.SC->getColonLoc(), " {"); |
177 | 7 | Pass.TA.insert(info.Range.getEnd(), "}\n"); |
178 | 7 | info.State = CaseInfo::St_Fixed; |
179 | 7 | } |
180 | | |
181 | 8 | bool hasVarReferencedOutside(CaseInfo &info) { |
182 | 39 | for (unsigned i = 0, e = LocalRefs.size(); i != e; ++i31 ) { |
183 | 32 | DeclRefExpr *DRE = LocalRefs[i]; |
184 | 32 | if (isInRange(DRE->getDecl()->getLocation(), info.Range) && |
185 | 32 | !isInRange(DRE->getLocation(), info.Range)4 ) |
186 | 1 | return true; |
187 | 32 | } |
188 | 7 | return false; |
189 | 8 | } |
190 | | |
191 | 289 | bool isInRange(SourceLocation Loc, SourceRange R) { |
192 | 289 | if (Loc.isInvalid()) |
193 | 0 | return false; |
194 | 289 | return !SM.isBeforeInTranslationUnit(Loc, R.getBegin()) && |
195 | 289 | SM.isBeforeInTranslationUnit(Loc, R.getEnd())132 ; |
196 | 289 | } |
197 | | }; |
198 | | |
199 | | } // anonymous namespace |
200 | | |
201 | 403 | void ProtectedScopeTraverser::traverseBody(BodyContext &BodyCtx) { |
202 | 403 | ProtectedScopeFixer Fix(BodyCtx); |
203 | 403 | } |