clang 19.0.0git
TransProtectedScope.cpp
Go to the documentation of this file.
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"
19
20using namespace clang;
21using namespace arcmt;
22using namespace trans;
23
24namespace {
25
26class LocalRefsCollector : public RecursiveASTVisitor<LocalRefsCollector> {
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
41struct CaseInfo {
42 SwitchCase *SC;
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
55class CaseCollector : public RecursiveASTVisitor<CaseCollector> {
56 ParentMap &PMap;
58
59public:
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
96class ProtectedScopeFixer {
97 MigrationPass &Pass;
101
102public:
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.
116 StoredDiags.append(DiagList.begin(), DiagList.end());
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(
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
202 ProtectedScopeFixer Fix(BodyCtx);
203}
Defines the clang::ASTContext interface.
NodeId Parent
Definition: ASTDiff.cpp:191
#define SM(sm)
Definition: Cuda.cpp:83
static DiagnosticBuilder Diag(DiagnosticsEngine *Diags, const LangOptions &Features, FullSourceLoc TokLoc, const char *TokBegin, const char *TokRangeBegin, const char *TokRangeEnd, unsigned DiagID)
Produce a diagnostic highlighting some portion of a literal.
SourceRange Range
Definition: SemaObjC.cpp:754
SourceLocation Loc
Definition: SemaObjC.cpp:755
Defines the SourceManager interface.
TextDiagnosticBuffer::DiagList DiagList
A reference to a declared variable, function, enum, etc.
Definition: Expr.h:1260
ValueDecl * getDecl()
Definition: Expr.h:1328
SourceLocation getLocation() const
Definition: Expr.h:1336
SourceLocation getLocation() const
Definition: DeclBase.h:445
Stmt * getParent(Stmt *) const
Definition: ParentMap.cpp:136
A class that does preorder or postorder depth-first traversal on the entire Clang AST and visits each...
Encodes a location in the source.
This class handles loading and caching of source files into memory.
A trivial tuple used to represent a source range.
SourceLocation getEnd() const
SourceLocation getBegin() const
Stmt - This represents one statement.
Definition: Stmt.h:84
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
Definition: Stmt.cpp:326
Represents a diagnostic in a form that can be retained until its corresponding source manager is dest...
Definition: Diagnostic.h:1695
SourceLocation getBeginLoc() const
Definition: Stmt.h:1788
const SwitchCase * getNextSwitchCase() const
Definition: Stmt.h:1774
SwitchStmt - This represents a 'switch' stmt.
Definition: Stmt.h:2388
Represent the declaration of a variable (in which case it is an lvalue) a function (in which case it ...
Definition: Decl.h:706
const CapturedDiagList & getDiags() const
Definition: Internals.h:163
TransformActions & TA
Definition: Internals.h:152
void insertAfterToken(SourceLocation loc, StringRef text)
void insert(SourceLocation loc, StringRef text)
bool clearDiagnostic(ArrayRef< unsigned > IDs, SourceRange range)
void traverseBody(BodyContext &BodyCtx) override
The JSON file list parser is used to communicate input to InstallAPI.