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"
16 #include "clang/AST/ASTContext.h"
19 
20 using namespace clang;
21 using namespace arcmt;
22 using namespace trans;
23 
24 namespace {
25 
26 class LocalRefsCollector : public RecursiveASTVisitor<LocalRefsCollector> {
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;
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;
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;
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.
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.
LineState State
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:152
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:1701
const SwitchCase * getNextSwitchCase() const
Definition: Stmt.h:1774
SourceLocation getBeginLoc() const
Definition: Stmt.h:1788
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:707
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.