xref: /llvm-project/clang/lib/ARCMigrate/TransProtectedScope.cpp (revision 86565c13094236e022d2238f5653641aaca7d31f)
103fbe3efSArgyrios Kyrtzidis //===--- TransProtectedScope.cpp - Transformations to ARC mode ------------===//
203fbe3efSArgyrios Kyrtzidis //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
603fbe3efSArgyrios Kyrtzidis //
703fbe3efSArgyrios Kyrtzidis //===----------------------------------------------------------------------===//
803fbe3efSArgyrios Kyrtzidis //
903fbe3efSArgyrios Kyrtzidis // Adds brackets in case statements that "contain" initialization of retaining
1003fbe3efSArgyrios Kyrtzidis // variable, thus emitting the "switch case is in protected scope" error.
1103fbe3efSArgyrios Kyrtzidis //
1203fbe3efSArgyrios Kyrtzidis //===----------------------------------------------------------------------===//
1303fbe3efSArgyrios Kyrtzidis 
1403fbe3efSArgyrios Kyrtzidis #include "Internals.h"
15*86565c13SReid Kleckner #include "Transforms.h"
1608281fa3SArgyrios Kyrtzidis #include "clang/AST/ASTContext.h"
17*86565c13SReid Kleckner #include "clang/Basic/SourceManager.h"
184b41745eSChandler Carruth #include "clang/Sema/SemaDiagnostic.h"
1903fbe3efSArgyrios Kyrtzidis 
2003fbe3efSArgyrios Kyrtzidis using namespace clang;
2103fbe3efSArgyrios Kyrtzidis using namespace arcmt;
2203fbe3efSArgyrios Kyrtzidis using namespace trans;
2303fbe3efSArgyrios Kyrtzidis 
2403fbe3efSArgyrios Kyrtzidis namespace {
2503fbe3efSArgyrios Kyrtzidis 
2608281fa3SArgyrios Kyrtzidis class LocalRefsCollector : public RecursiveASTVisitor<LocalRefsCollector> {
2708281fa3SArgyrios Kyrtzidis   SmallVectorImpl<DeclRefExpr *> &Refs;
2808281fa3SArgyrios Kyrtzidis 
2908281fa3SArgyrios Kyrtzidis public:
LocalRefsCollector(SmallVectorImpl<DeclRefExpr * > & refs)3008281fa3SArgyrios Kyrtzidis   LocalRefsCollector(SmallVectorImpl<DeclRefExpr *> &refs)
3108281fa3SArgyrios Kyrtzidis     : Refs(refs) { }
3208281fa3SArgyrios Kyrtzidis 
VisitDeclRefExpr(DeclRefExpr * E)3308281fa3SArgyrios Kyrtzidis   bool VisitDeclRefExpr(DeclRefExpr *E) {
3408281fa3SArgyrios Kyrtzidis     if (ValueDecl *D = E->getDecl())
3508281fa3SArgyrios Kyrtzidis       if (D->getDeclContext()->getRedeclContext()->isFunctionOrMethod())
3608281fa3SArgyrios Kyrtzidis         Refs.push_back(E);
3708281fa3SArgyrios Kyrtzidis     return true;
3808281fa3SArgyrios Kyrtzidis   }
3908281fa3SArgyrios Kyrtzidis };
4008281fa3SArgyrios Kyrtzidis 
4103fbe3efSArgyrios Kyrtzidis struct CaseInfo {
4203fbe3efSArgyrios Kyrtzidis   SwitchCase *SC;
4303fbe3efSArgyrios Kyrtzidis   SourceRange Range;
4408281fa3SArgyrios Kyrtzidis   enum {
4508281fa3SArgyrios Kyrtzidis     St_Unchecked,
4608281fa3SArgyrios Kyrtzidis     St_CannotFix,
4708281fa3SArgyrios Kyrtzidis     St_Fixed
4808281fa3SArgyrios Kyrtzidis   } State;
4903fbe3efSArgyrios Kyrtzidis 
CaseInfo__anonf9ad4e870111::CaseInfo508ae12039SCraig Topper   CaseInfo() : SC(nullptr), State(St_Unchecked) {}
CaseInfo__anonf9ad4e870111::CaseInfo5103fbe3efSArgyrios Kyrtzidis   CaseInfo(SwitchCase *S, SourceRange Range)
5208281fa3SArgyrios Kyrtzidis     : SC(S), Range(Range), State(St_Unchecked) {}
5303fbe3efSArgyrios Kyrtzidis };
5403fbe3efSArgyrios Kyrtzidis 
5503fbe3efSArgyrios Kyrtzidis class CaseCollector : public RecursiveASTVisitor<CaseCollector> {
5608281fa3SArgyrios Kyrtzidis   ParentMap &PMap;
57f857950dSDmitri Gribenko   SmallVectorImpl<CaseInfo> &Cases;
5803fbe3efSArgyrios Kyrtzidis 
5903fbe3efSArgyrios Kyrtzidis public:
CaseCollector(ParentMap & PMap,SmallVectorImpl<CaseInfo> & Cases)60f857950dSDmitri Gribenko   CaseCollector(ParentMap &PMap, SmallVectorImpl<CaseInfo> &Cases)
6108281fa3SArgyrios Kyrtzidis     : PMap(PMap), Cases(Cases) { }
6203fbe3efSArgyrios Kyrtzidis 
VisitSwitchStmt(SwitchStmt * S)6303fbe3efSArgyrios Kyrtzidis   bool VisitSwitchStmt(SwitchStmt *S) {
6403fbe3efSArgyrios Kyrtzidis     SwitchCase *Curr = S->getSwitchCaseList();
6508281fa3SArgyrios Kyrtzidis     if (!Curr)
6608281fa3SArgyrios Kyrtzidis       return true;
6708281fa3SArgyrios Kyrtzidis     Stmt *Parent = getCaseParent(Curr);
6808281fa3SArgyrios Kyrtzidis     Curr = Curr->getNextSwitchCase();
6908281fa3SArgyrios Kyrtzidis     // Make sure all case statements are in the same scope.
7008281fa3SArgyrios Kyrtzidis     while (Curr) {
7108281fa3SArgyrios Kyrtzidis       if (getCaseParent(Curr) != Parent)
7208281fa3SArgyrios Kyrtzidis         return true;
7308281fa3SArgyrios Kyrtzidis       Curr = Curr->getNextSwitchCase();
7408281fa3SArgyrios Kyrtzidis     }
7508281fa3SArgyrios Kyrtzidis 
761c301dcbSStephen Kelly     SourceLocation NextLoc = S->getEndLoc();
7708281fa3SArgyrios Kyrtzidis     Curr = S->getSwitchCaseList();
7803fbe3efSArgyrios Kyrtzidis     // We iterate over case statements in reverse source-order.
7903fbe3efSArgyrios Kyrtzidis     while (Curr) {
80f2ceec48SStephen Kelly       Cases.push_back(
81f2ceec48SStephen Kelly           CaseInfo(Curr, SourceRange(Curr->getBeginLoc(), NextLoc)));
82f2ceec48SStephen Kelly       NextLoc = Curr->getBeginLoc();
8303fbe3efSArgyrios Kyrtzidis       Curr = Curr->getNextSwitchCase();
8403fbe3efSArgyrios Kyrtzidis     }
8503fbe3efSArgyrios Kyrtzidis     return true;
8603fbe3efSArgyrios Kyrtzidis   }
8708281fa3SArgyrios Kyrtzidis 
getCaseParent(SwitchCase * S)8808281fa3SArgyrios Kyrtzidis   Stmt *getCaseParent(SwitchCase *S) {
8908281fa3SArgyrios Kyrtzidis     Stmt *Parent = PMap.getParent(S);
9008281fa3SArgyrios Kyrtzidis     while (Parent && (isa<SwitchCase>(Parent) || isa<LabelStmt>(Parent)))
9108281fa3SArgyrios Kyrtzidis       Parent = PMap.getParent(Parent);
9208281fa3SArgyrios Kyrtzidis     return Parent;
9308281fa3SArgyrios Kyrtzidis   }
9403fbe3efSArgyrios Kyrtzidis };
9503fbe3efSArgyrios Kyrtzidis 
9608281fa3SArgyrios Kyrtzidis class ProtectedScopeFixer {
9708281fa3SArgyrios Kyrtzidis   MigrationPass &Pass;
9808281fa3SArgyrios Kyrtzidis   SourceManager &SM;
9903fbe3efSArgyrios Kyrtzidis   SmallVector<CaseInfo, 16> Cases;
10008281fa3SArgyrios Kyrtzidis   SmallVector<DeclRefExpr *, 16> LocalRefs;
10108281fa3SArgyrios Kyrtzidis 
10208281fa3SArgyrios Kyrtzidis public:
ProtectedScopeFixer(BodyContext & BodyCtx)10308281fa3SArgyrios Kyrtzidis   ProtectedScopeFixer(BodyContext &BodyCtx)
10408281fa3SArgyrios Kyrtzidis     : Pass(BodyCtx.getMigrationContext().Pass),
10508281fa3SArgyrios Kyrtzidis       SM(Pass.Ctx.getSourceManager()) {
10608281fa3SArgyrios Kyrtzidis 
10708281fa3SArgyrios Kyrtzidis     CaseCollector(BodyCtx.getParentMap(), Cases)
10808281fa3SArgyrios Kyrtzidis         .TraverseStmt(BodyCtx.getTopStmt());
10908281fa3SArgyrios Kyrtzidis     LocalRefsCollector(LocalRefs).TraverseStmt(BodyCtx.getTopStmt());
11003fbe3efSArgyrios Kyrtzidis 
11103fbe3efSArgyrios Kyrtzidis     SourceRange BodyRange = BodyCtx.getTopStmt()->getSourceRange();
11203fbe3efSArgyrios Kyrtzidis     const CapturedDiagList &DiagList = Pass.getDiags();
113d2c0abadSArgyrios Kyrtzidis     // Copy the diagnostics so we don't have to worry about invaliding iterators
114d2c0abadSArgyrios Kyrtzidis     // from the diagnostic list.
115d2c0abadSArgyrios Kyrtzidis     SmallVector<StoredDiagnostic, 16> StoredDiags;
116d2c0abadSArgyrios Kyrtzidis     StoredDiags.append(DiagList.begin(), DiagList.end());
117d2c0abadSArgyrios Kyrtzidis     SmallVectorImpl<StoredDiagnostic>::iterator
118d2c0abadSArgyrios Kyrtzidis         I = StoredDiags.begin(), E = StoredDiags.end();
11903fbe3efSArgyrios Kyrtzidis     while (I != E) {
12003fbe3efSArgyrios Kyrtzidis       if (I->getID() == diag::err_switch_into_protected_scope &&
12103fbe3efSArgyrios Kyrtzidis           isInRange(I->getLocation(), BodyRange)) {
12208281fa3SArgyrios Kyrtzidis         handleProtectedScopeError(I, E);
12303fbe3efSArgyrios Kyrtzidis         continue;
12403fbe3efSArgyrios Kyrtzidis       }
12503fbe3efSArgyrios Kyrtzidis       ++I;
12603fbe3efSArgyrios Kyrtzidis     }
12703fbe3efSArgyrios Kyrtzidis   }
12808281fa3SArgyrios Kyrtzidis 
handleProtectedScopeError(SmallVectorImpl<StoredDiagnostic>::iterator & DiagI,SmallVectorImpl<StoredDiagnostic>::iterator DiagE)129d2c0abadSArgyrios Kyrtzidis   void handleProtectedScopeError(
130d2c0abadSArgyrios Kyrtzidis                              SmallVectorImpl<StoredDiagnostic>::iterator &DiagI,
131d2c0abadSArgyrios Kyrtzidis                              SmallVectorImpl<StoredDiagnostic>::iterator DiagE){
13208281fa3SArgyrios Kyrtzidis     Transaction Trans(Pass.TA);
13308281fa3SArgyrios Kyrtzidis     assert(DiagI->getID() == diag::err_switch_into_protected_scope);
13408281fa3SArgyrios Kyrtzidis     SourceLocation ErrLoc = DiagI->getLocation();
13508281fa3SArgyrios Kyrtzidis     bool handledAllNotes = true;
13608281fa3SArgyrios Kyrtzidis     ++DiagI;
13708281fa3SArgyrios Kyrtzidis     for (; DiagI != DiagE && DiagI->getLevel() == DiagnosticsEngine::Note;
13808281fa3SArgyrios Kyrtzidis          ++DiagI) {
13908281fa3SArgyrios Kyrtzidis       if (!handleProtectedNote(*DiagI))
14008281fa3SArgyrios Kyrtzidis         handledAllNotes = false;
14108281fa3SArgyrios Kyrtzidis     }
14208281fa3SArgyrios Kyrtzidis 
14308281fa3SArgyrios Kyrtzidis     if (handledAllNotes)
14408281fa3SArgyrios Kyrtzidis       Pass.TA.clearDiagnostic(diag::err_switch_into_protected_scope, ErrLoc);
14508281fa3SArgyrios Kyrtzidis   }
14608281fa3SArgyrios Kyrtzidis 
handleProtectedNote(const StoredDiagnostic & Diag)14708281fa3SArgyrios Kyrtzidis   bool handleProtectedNote(const StoredDiagnostic &Diag) {
14808281fa3SArgyrios Kyrtzidis     assert(Diag.getLevel() == DiagnosticsEngine::Note);
14908281fa3SArgyrios Kyrtzidis 
15008281fa3SArgyrios Kyrtzidis     for (unsigned i = 0; i != Cases.size(); i++) {
15108281fa3SArgyrios Kyrtzidis       CaseInfo &info = Cases[i];
15208281fa3SArgyrios Kyrtzidis       if (isInRange(Diag.getLocation(), info.Range)) {
15308281fa3SArgyrios Kyrtzidis 
15408281fa3SArgyrios Kyrtzidis         if (info.State == CaseInfo::St_Unchecked)
15508281fa3SArgyrios Kyrtzidis           tryFixing(info);
15608281fa3SArgyrios Kyrtzidis         assert(info.State != CaseInfo::St_Unchecked);
15708281fa3SArgyrios Kyrtzidis 
15808281fa3SArgyrios Kyrtzidis         if (info.State == CaseInfo::St_Fixed) {
15908281fa3SArgyrios Kyrtzidis           Pass.TA.clearDiagnostic(Diag.getID(), Diag.getLocation());
16008281fa3SArgyrios Kyrtzidis           return true;
16108281fa3SArgyrios Kyrtzidis         }
16208281fa3SArgyrios Kyrtzidis         return false;
16308281fa3SArgyrios Kyrtzidis       }
16408281fa3SArgyrios Kyrtzidis     }
16508281fa3SArgyrios Kyrtzidis 
16608281fa3SArgyrios Kyrtzidis     return false;
16708281fa3SArgyrios Kyrtzidis   }
16808281fa3SArgyrios Kyrtzidis 
tryFixing(CaseInfo & info)16908281fa3SArgyrios Kyrtzidis   void tryFixing(CaseInfo &info) {
17008281fa3SArgyrios Kyrtzidis     assert(info.State == CaseInfo::St_Unchecked);
17108281fa3SArgyrios Kyrtzidis     if (hasVarReferencedOutside(info)) {
17208281fa3SArgyrios Kyrtzidis       info.State = CaseInfo::St_CannotFix;
17308281fa3SArgyrios Kyrtzidis       return;
17408281fa3SArgyrios Kyrtzidis     }
17508281fa3SArgyrios Kyrtzidis 
17608281fa3SArgyrios Kyrtzidis     Pass.TA.insertAfterToken(info.SC->getColonLoc(), " {");
17708281fa3SArgyrios Kyrtzidis     Pass.TA.insert(info.Range.getEnd(), "}\n");
17808281fa3SArgyrios Kyrtzidis     info.State = CaseInfo::St_Fixed;
17908281fa3SArgyrios Kyrtzidis   }
18008281fa3SArgyrios Kyrtzidis 
hasVarReferencedOutside(CaseInfo & info)18108281fa3SArgyrios Kyrtzidis   bool hasVarReferencedOutside(CaseInfo &info) {
18208281fa3SArgyrios Kyrtzidis     for (unsigned i = 0, e = LocalRefs.size(); i != e; ++i) {
18308281fa3SArgyrios Kyrtzidis       DeclRefExpr *DRE = LocalRefs[i];
18408281fa3SArgyrios Kyrtzidis       if (isInRange(DRE->getDecl()->getLocation(), info.Range) &&
18508281fa3SArgyrios Kyrtzidis           !isInRange(DRE->getLocation(), info.Range))
18608281fa3SArgyrios Kyrtzidis         return true;
18708281fa3SArgyrios Kyrtzidis     }
18808281fa3SArgyrios Kyrtzidis     return false;
18908281fa3SArgyrios Kyrtzidis   }
19008281fa3SArgyrios Kyrtzidis 
isInRange(SourceLocation Loc,SourceRange R)19108281fa3SArgyrios Kyrtzidis   bool isInRange(SourceLocation Loc, SourceRange R) {
19208281fa3SArgyrios Kyrtzidis     if (Loc.isInvalid())
19308281fa3SArgyrios Kyrtzidis       return false;
19408281fa3SArgyrios Kyrtzidis     return !SM.isBeforeInTranslationUnit(Loc, R.getBegin()) &&
19508281fa3SArgyrios Kyrtzidis             SM.isBeforeInTranslationUnit(Loc, R.getEnd());
19608281fa3SArgyrios Kyrtzidis   }
19708281fa3SArgyrios Kyrtzidis };
19808281fa3SArgyrios Kyrtzidis 
19908281fa3SArgyrios Kyrtzidis } // anonymous namespace
20008281fa3SArgyrios Kyrtzidis 
traverseBody(BodyContext & BodyCtx)20108281fa3SArgyrios Kyrtzidis void ProtectedScopeTraverser::traverseBody(BodyContext &BodyCtx) {
20208281fa3SArgyrios Kyrtzidis   ProtectedScopeFixer Fix(BodyCtx);
20308281fa3SArgyrios Kyrtzidis }
204