xref: /freebsd-src/contrib/llvm-project/clang/lib/ARCMigrate/TransProtectedScope.cpp (revision e25152834cdf3b353892835a4f3b157e066a8ed4)
10b57cec5SDimitry Andric //===--- TransProtectedScope.cpp - Transformations to ARC mode ------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // Adds brackets in case statements that "contain" initialization of retaining
100b57cec5SDimitry Andric // variable, thus emitting the "switch case is in protected scope" error.
110b57cec5SDimitry Andric //
120b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
130b57cec5SDimitry Andric 
140b57cec5SDimitry Andric #include "Internals.h"
15*5ffd83dbSDimitry Andric #include "Transforms.h"
160b57cec5SDimitry Andric #include "clang/AST/ASTContext.h"
17*5ffd83dbSDimitry Andric #include "clang/Basic/SourceManager.h"
180b57cec5SDimitry Andric #include "clang/Sema/SemaDiagnostic.h"
190b57cec5SDimitry Andric 
200b57cec5SDimitry Andric using namespace clang;
210b57cec5SDimitry Andric using namespace arcmt;
220b57cec5SDimitry Andric using namespace trans;
230b57cec5SDimitry Andric 
240b57cec5SDimitry Andric namespace {
250b57cec5SDimitry Andric 
260b57cec5SDimitry Andric class LocalRefsCollector : public RecursiveASTVisitor<LocalRefsCollector> {
270b57cec5SDimitry Andric   SmallVectorImpl<DeclRefExpr *> &Refs;
280b57cec5SDimitry Andric 
290b57cec5SDimitry Andric public:
LocalRefsCollector(SmallVectorImpl<DeclRefExpr * > & refs)300b57cec5SDimitry Andric   LocalRefsCollector(SmallVectorImpl<DeclRefExpr *> &refs)
310b57cec5SDimitry Andric     : Refs(refs) { }
320b57cec5SDimitry Andric 
VisitDeclRefExpr(DeclRefExpr * E)330b57cec5SDimitry Andric   bool VisitDeclRefExpr(DeclRefExpr *E) {
340b57cec5SDimitry Andric     if (ValueDecl *D = E->getDecl())
350b57cec5SDimitry Andric       if (D->getDeclContext()->getRedeclContext()->isFunctionOrMethod())
360b57cec5SDimitry Andric         Refs.push_back(E);
370b57cec5SDimitry Andric     return true;
380b57cec5SDimitry Andric   }
390b57cec5SDimitry Andric };
400b57cec5SDimitry Andric 
410b57cec5SDimitry Andric struct CaseInfo {
420b57cec5SDimitry Andric   SwitchCase *SC;
430b57cec5SDimitry Andric   SourceRange Range;
440b57cec5SDimitry Andric   enum {
450b57cec5SDimitry Andric     St_Unchecked,
460b57cec5SDimitry Andric     St_CannotFix,
470b57cec5SDimitry Andric     St_Fixed
480b57cec5SDimitry Andric   } State;
490b57cec5SDimitry Andric 
CaseInfo__anon1e0df8460111::CaseInfo500b57cec5SDimitry Andric   CaseInfo() : SC(nullptr), State(St_Unchecked) {}
CaseInfo__anon1e0df8460111::CaseInfo510b57cec5SDimitry Andric   CaseInfo(SwitchCase *S, SourceRange Range)
520b57cec5SDimitry Andric     : SC(S), Range(Range), State(St_Unchecked) {}
530b57cec5SDimitry Andric };
540b57cec5SDimitry Andric 
550b57cec5SDimitry Andric class CaseCollector : public RecursiveASTVisitor<CaseCollector> {
560b57cec5SDimitry Andric   ParentMap &PMap;
570b57cec5SDimitry Andric   SmallVectorImpl<CaseInfo> &Cases;
580b57cec5SDimitry Andric 
590b57cec5SDimitry Andric public:
CaseCollector(ParentMap & PMap,SmallVectorImpl<CaseInfo> & Cases)600b57cec5SDimitry Andric   CaseCollector(ParentMap &PMap, SmallVectorImpl<CaseInfo> &Cases)
610b57cec5SDimitry Andric     : PMap(PMap), Cases(Cases) { }
620b57cec5SDimitry Andric 
VisitSwitchStmt(SwitchStmt * S)630b57cec5SDimitry Andric   bool VisitSwitchStmt(SwitchStmt *S) {
640b57cec5SDimitry Andric     SwitchCase *Curr = S->getSwitchCaseList();
650b57cec5SDimitry Andric     if (!Curr)
660b57cec5SDimitry Andric       return true;
670b57cec5SDimitry Andric     Stmt *Parent = getCaseParent(Curr);
680b57cec5SDimitry Andric     Curr = Curr->getNextSwitchCase();
690b57cec5SDimitry Andric     // Make sure all case statements are in the same scope.
700b57cec5SDimitry Andric     while (Curr) {
710b57cec5SDimitry Andric       if (getCaseParent(Curr) != Parent)
720b57cec5SDimitry Andric         return true;
730b57cec5SDimitry Andric       Curr = Curr->getNextSwitchCase();
740b57cec5SDimitry Andric     }
750b57cec5SDimitry Andric 
760b57cec5SDimitry Andric     SourceLocation NextLoc = S->getEndLoc();
770b57cec5SDimitry Andric     Curr = S->getSwitchCaseList();
780b57cec5SDimitry Andric     // We iterate over case statements in reverse source-order.
790b57cec5SDimitry Andric     while (Curr) {
800b57cec5SDimitry Andric       Cases.push_back(
810b57cec5SDimitry Andric           CaseInfo(Curr, SourceRange(Curr->getBeginLoc(), NextLoc)));
820b57cec5SDimitry Andric       NextLoc = Curr->getBeginLoc();
830b57cec5SDimitry Andric       Curr = Curr->getNextSwitchCase();
840b57cec5SDimitry Andric     }
850b57cec5SDimitry Andric     return true;
860b57cec5SDimitry Andric   }
870b57cec5SDimitry Andric 
getCaseParent(SwitchCase * S)880b57cec5SDimitry Andric   Stmt *getCaseParent(SwitchCase *S) {
890b57cec5SDimitry Andric     Stmt *Parent = PMap.getParent(S);
900b57cec5SDimitry Andric     while (Parent && (isa<SwitchCase>(Parent) || isa<LabelStmt>(Parent)))
910b57cec5SDimitry Andric       Parent = PMap.getParent(Parent);
920b57cec5SDimitry Andric     return Parent;
930b57cec5SDimitry Andric   }
940b57cec5SDimitry Andric };
950b57cec5SDimitry Andric 
960b57cec5SDimitry Andric class ProtectedScopeFixer {
970b57cec5SDimitry Andric   MigrationPass &Pass;
980b57cec5SDimitry Andric   SourceManager &SM;
990b57cec5SDimitry Andric   SmallVector<CaseInfo, 16> Cases;
1000b57cec5SDimitry Andric   SmallVector<DeclRefExpr *, 16> LocalRefs;
1010b57cec5SDimitry Andric 
1020b57cec5SDimitry Andric public:
ProtectedScopeFixer(BodyContext & BodyCtx)1030b57cec5SDimitry Andric   ProtectedScopeFixer(BodyContext &BodyCtx)
1040b57cec5SDimitry Andric     : Pass(BodyCtx.getMigrationContext().Pass),
1050b57cec5SDimitry Andric       SM(Pass.Ctx.getSourceManager()) {
1060b57cec5SDimitry Andric 
1070b57cec5SDimitry Andric     CaseCollector(BodyCtx.getParentMap(), Cases)
1080b57cec5SDimitry Andric         .TraverseStmt(BodyCtx.getTopStmt());
1090b57cec5SDimitry Andric     LocalRefsCollector(LocalRefs).TraverseStmt(BodyCtx.getTopStmt());
1100b57cec5SDimitry Andric 
1110b57cec5SDimitry Andric     SourceRange BodyRange = BodyCtx.getTopStmt()->getSourceRange();
1120b57cec5SDimitry Andric     const CapturedDiagList &DiagList = Pass.getDiags();
1130b57cec5SDimitry Andric     // Copy the diagnostics so we don't have to worry about invaliding iterators
1140b57cec5SDimitry Andric     // from the diagnostic list.
1150b57cec5SDimitry Andric     SmallVector<StoredDiagnostic, 16> StoredDiags;
1160b57cec5SDimitry Andric     StoredDiags.append(DiagList.begin(), DiagList.end());
1170b57cec5SDimitry Andric     SmallVectorImpl<StoredDiagnostic>::iterator
1180b57cec5SDimitry Andric         I = StoredDiags.begin(), E = StoredDiags.end();
1190b57cec5SDimitry Andric     while (I != E) {
1200b57cec5SDimitry Andric       if (I->getID() == diag::err_switch_into_protected_scope &&
1210b57cec5SDimitry Andric           isInRange(I->getLocation(), BodyRange)) {
1220b57cec5SDimitry Andric         handleProtectedScopeError(I, E);
1230b57cec5SDimitry Andric         continue;
1240b57cec5SDimitry Andric       }
1250b57cec5SDimitry Andric       ++I;
1260b57cec5SDimitry Andric     }
1270b57cec5SDimitry Andric   }
1280b57cec5SDimitry Andric 
handleProtectedScopeError(SmallVectorImpl<StoredDiagnostic>::iterator & DiagI,SmallVectorImpl<StoredDiagnostic>::iterator DiagE)1290b57cec5SDimitry Andric   void handleProtectedScopeError(
1300b57cec5SDimitry Andric                              SmallVectorImpl<StoredDiagnostic>::iterator &DiagI,
1310b57cec5SDimitry Andric                              SmallVectorImpl<StoredDiagnostic>::iterator DiagE){
1320b57cec5SDimitry Andric     Transaction Trans(Pass.TA);
1330b57cec5SDimitry Andric     assert(DiagI->getID() == diag::err_switch_into_protected_scope);
1340b57cec5SDimitry Andric     SourceLocation ErrLoc = DiagI->getLocation();
1350b57cec5SDimitry Andric     bool handledAllNotes = true;
1360b57cec5SDimitry Andric     ++DiagI;
1370b57cec5SDimitry Andric     for (; DiagI != DiagE && DiagI->getLevel() == DiagnosticsEngine::Note;
1380b57cec5SDimitry Andric          ++DiagI) {
1390b57cec5SDimitry Andric       if (!handleProtectedNote(*DiagI))
1400b57cec5SDimitry Andric         handledAllNotes = false;
1410b57cec5SDimitry Andric     }
1420b57cec5SDimitry Andric 
1430b57cec5SDimitry Andric     if (handledAllNotes)
1440b57cec5SDimitry Andric       Pass.TA.clearDiagnostic(diag::err_switch_into_protected_scope, ErrLoc);
1450b57cec5SDimitry Andric   }
1460b57cec5SDimitry Andric 
handleProtectedNote(const StoredDiagnostic & Diag)1470b57cec5SDimitry Andric   bool handleProtectedNote(const StoredDiagnostic &Diag) {
1480b57cec5SDimitry Andric     assert(Diag.getLevel() == DiagnosticsEngine::Note);
1490b57cec5SDimitry Andric 
1500b57cec5SDimitry Andric     for (unsigned i = 0; i != Cases.size(); i++) {
1510b57cec5SDimitry Andric       CaseInfo &info = Cases[i];
1520b57cec5SDimitry Andric       if (isInRange(Diag.getLocation(), info.Range)) {
1530b57cec5SDimitry Andric 
1540b57cec5SDimitry Andric         if (info.State == CaseInfo::St_Unchecked)
1550b57cec5SDimitry Andric           tryFixing(info);
1560b57cec5SDimitry Andric         assert(info.State != CaseInfo::St_Unchecked);
1570b57cec5SDimitry Andric 
1580b57cec5SDimitry Andric         if (info.State == CaseInfo::St_Fixed) {
1590b57cec5SDimitry Andric           Pass.TA.clearDiagnostic(Diag.getID(), Diag.getLocation());
1600b57cec5SDimitry Andric           return true;
1610b57cec5SDimitry Andric         }
1620b57cec5SDimitry Andric         return false;
1630b57cec5SDimitry Andric       }
1640b57cec5SDimitry Andric     }
1650b57cec5SDimitry Andric 
1660b57cec5SDimitry Andric     return false;
1670b57cec5SDimitry Andric   }
1680b57cec5SDimitry Andric 
tryFixing(CaseInfo & info)1690b57cec5SDimitry Andric   void tryFixing(CaseInfo &info) {
1700b57cec5SDimitry Andric     assert(info.State == CaseInfo::St_Unchecked);
1710b57cec5SDimitry Andric     if (hasVarReferencedOutside(info)) {
1720b57cec5SDimitry Andric       info.State = CaseInfo::St_CannotFix;
1730b57cec5SDimitry Andric       return;
1740b57cec5SDimitry Andric     }
1750b57cec5SDimitry Andric 
1760b57cec5SDimitry Andric     Pass.TA.insertAfterToken(info.SC->getColonLoc(), " {");
1770b57cec5SDimitry Andric     Pass.TA.insert(info.Range.getEnd(), "}\n");
1780b57cec5SDimitry Andric     info.State = CaseInfo::St_Fixed;
1790b57cec5SDimitry Andric   }
1800b57cec5SDimitry Andric 
hasVarReferencedOutside(CaseInfo & info)1810b57cec5SDimitry Andric   bool hasVarReferencedOutside(CaseInfo &info) {
1820b57cec5SDimitry Andric     for (unsigned i = 0, e = LocalRefs.size(); i != e; ++i) {
1830b57cec5SDimitry Andric       DeclRefExpr *DRE = LocalRefs[i];
1840b57cec5SDimitry Andric       if (isInRange(DRE->getDecl()->getLocation(), info.Range) &&
1850b57cec5SDimitry Andric           !isInRange(DRE->getLocation(), info.Range))
1860b57cec5SDimitry Andric         return true;
1870b57cec5SDimitry Andric     }
1880b57cec5SDimitry Andric     return false;
1890b57cec5SDimitry Andric   }
1900b57cec5SDimitry Andric 
isInRange(SourceLocation Loc,SourceRange R)1910b57cec5SDimitry Andric   bool isInRange(SourceLocation Loc, SourceRange R) {
1920b57cec5SDimitry Andric     if (Loc.isInvalid())
1930b57cec5SDimitry Andric       return false;
1940b57cec5SDimitry Andric     return !SM.isBeforeInTranslationUnit(Loc, R.getBegin()) &&
1950b57cec5SDimitry Andric             SM.isBeforeInTranslationUnit(Loc, R.getEnd());
1960b57cec5SDimitry Andric   }
1970b57cec5SDimitry Andric };
1980b57cec5SDimitry Andric 
1990b57cec5SDimitry Andric } // anonymous namespace
2000b57cec5SDimitry Andric 
traverseBody(BodyContext & BodyCtx)2010b57cec5SDimitry Andric void ProtectedScopeTraverser::traverseBody(BodyContext &BodyCtx) {
2020b57cec5SDimitry Andric   ProtectedScopeFixer Fix(BodyCtx);
2030b57cec5SDimitry Andric }
204