xref: /netbsd-src/external/apache2/llvm/dist/clang/lib/ARCMigrate/TransProtectedScope.cpp (revision e038c9c4676b0f19b1b7dd08a940c6ed64a6d5ae)
17330f729Sjoerg //===--- TransProtectedScope.cpp - Transformations to ARC mode ------------===//
27330f729Sjoerg //
37330f729Sjoerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
47330f729Sjoerg // See https://llvm.org/LICENSE.txt for license information.
57330f729Sjoerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
67330f729Sjoerg //
77330f729Sjoerg //===----------------------------------------------------------------------===//
87330f729Sjoerg //
97330f729Sjoerg // Adds brackets in case statements that "contain" initialization of retaining
107330f729Sjoerg // variable, thus emitting the "switch case is in protected scope" error.
117330f729Sjoerg //
127330f729Sjoerg //===----------------------------------------------------------------------===//
137330f729Sjoerg 
147330f729Sjoerg #include "Internals.h"
15*e038c9c4Sjoerg #include "Transforms.h"
167330f729Sjoerg #include "clang/AST/ASTContext.h"
17*e038c9c4Sjoerg #include "clang/Basic/SourceManager.h"
187330f729Sjoerg #include "clang/Sema/SemaDiagnostic.h"
197330f729Sjoerg 
207330f729Sjoerg using namespace clang;
217330f729Sjoerg using namespace arcmt;
227330f729Sjoerg using namespace trans;
237330f729Sjoerg 
247330f729Sjoerg namespace {
257330f729Sjoerg 
267330f729Sjoerg class LocalRefsCollector : public RecursiveASTVisitor<LocalRefsCollector> {
277330f729Sjoerg   SmallVectorImpl<DeclRefExpr *> &Refs;
287330f729Sjoerg 
297330f729Sjoerg public:
LocalRefsCollector(SmallVectorImpl<DeclRefExpr * > & refs)307330f729Sjoerg   LocalRefsCollector(SmallVectorImpl<DeclRefExpr *> &refs)
317330f729Sjoerg     : Refs(refs) { }
327330f729Sjoerg 
VisitDeclRefExpr(DeclRefExpr * E)337330f729Sjoerg   bool VisitDeclRefExpr(DeclRefExpr *E) {
347330f729Sjoerg     if (ValueDecl *D = E->getDecl())
357330f729Sjoerg       if (D->getDeclContext()->getRedeclContext()->isFunctionOrMethod())
367330f729Sjoerg         Refs.push_back(E);
377330f729Sjoerg     return true;
387330f729Sjoerg   }
397330f729Sjoerg };
407330f729Sjoerg 
417330f729Sjoerg struct CaseInfo {
427330f729Sjoerg   SwitchCase *SC;
437330f729Sjoerg   SourceRange Range;
447330f729Sjoerg   enum {
457330f729Sjoerg     St_Unchecked,
467330f729Sjoerg     St_CannotFix,
477330f729Sjoerg     St_Fixed
487330f729Sjoerg   } State;
497330f729Sjoerg 
CaseInfo__anon8c18559f0111::CaseInfo507330f729Sjoerg   CaseInfo() : SC(nullptr), State(St_Unchecked) {}
CaseInfo__anon8c18559f0111::CaseInfo517330f729Sjoerg   CaseInfo(SwitchCase *S, SourceRange Range)
527330f729Sjoerg     : SC(S), Range(Range), State(St_Unchecked) {}
537330f729Sjoerg };
547330f729Sjoerg 
557330f729Sjoerg class CaseCollector : public RecursiveASTVisitor<CaseCollector> {
567330f729Sjoerg   ParentMap &PMap;
577330f729Sjoerg   SmallVectorImpl<CaseInfo> &Cases;
587330f729Sjoerg 
597330f729Sjoerg public:
CaseCollector(ParentMap & PMap,SmallVectorImpl<CaseInfo> & Cases)607330f729Sjoerg   CaseCollector(ParentMap &PMap, SmallVectorImpl<CaseInfo> &Cases)
617330f729Sjoerg     : PMap(PMap), Cases(Cases) { }
627330f729Sjoerg 
VisitSwitchStmt(SwitchStmt * S)637330f729Sjoerg   bool VisitSwitchStmt(SwitchStmt *S) {
647330f729Sjoerg     SwitchCase *Curr = S->getSwitchCaseList();
657330f729Sjoerg     if (!Curr)
667330f729Sjoerg       return true;
677330f729Sjoerg     Stmt *Parent = getCaseParent(Curr);
687330f729Sjoerg     Curr = Curr->getNextSwitchCase();
697330f729Sjoerg     // Make sure all case statements are in the same scope.
707330f729Sjoerg     while (Curr) {
717330f729Sjoerg       if (getCaseParent(Curr) != Parent)
727330f729Sjoerg         return true;
737330f729Sjoerg       Curr = Curr->getNextSwitchCase();
747330f729Sjoerg     }
757330f729Sjoerg 
767330f729Sjoerg     SourceLocation NextLoc = S->getEndLoc();
777330f729Sjoerg     Curr = S->getSwitchCaseList();
787330f729Sjoerg     // We iterate over case statements in reverse source-order.
797330f729Sjoerg     while (Curr) {
807330f729Sjoerg       Cases.push_back(
817330f729Sjoerg           CaseInfo(Curr, SourceRange(Curr->getBeginLoc(), NextLoc)));
827330f729Sjoerg       NextLoc = Curr->getBeginLoc();
837330f729Sjoerg       Curr = Curr->getNextSwitchCase();
847330f729Sjoerg     }
857330f729Sjoerg     return true;
867330f729Sjoerg   }
877330f729Sjoerg 
getCaseParent(SwitchCase * S)887330f729Sjoerg   Stmt *getCaseParent(SwitchCase *S) {
897330f729Sjoerg     Stmt *Parent = PMap.getParent(S);
907330f729Sjoerg     while (Parent && (isa<SwitchCase>(Parent) || isa<LabelStmt>(Parent)))
917330f729Sjoerg       Parent = PMap.getParent(Parent);
927330f729Sjoerg     return Parent;
937330f729Sjoerg   }
947330f729Sjoerg };
957330f729Sjoerg 
967330f729Sjoerg class ProtectedScopeFixer {
977330f729Sjoerg   MigrationPass &Pass;
987330f729Sjoerg   SourceManager &SM;
997330f729Sjoerg   SmallVector<CaseInfo, 16> Cases;
1007330f729Sjoerg   SmallVector<DeclRefExpr *, 16> LocalRefs;
1017330f729Sjoerg 
1027330f729Sjoerg public:
ProtectedScopeFixer(BodyContext & BodyCtx)1037330f729Sjoerg   ProtectedScopeFixer(BodyContext &BodyCtx)
1047330f729Sjoerg     : Pass(BodyCtx.getMigrationContext().Pass),
1057330f729Sjoerg       SM(Pass.Ctx.getSourceManager()) {
1067330f729Sjoerg 
1077330f729Sjoerg     CaseCollector(BodyCtx.getParentMap(), Cases)
1087330f729Sjoerg         .TraverseStmt(BodyCtx.getTopStmt());
1097330f729Sjoerg     LocalRefsCollector(LocalRefs).TraverseStmt(BodyCtx.getTopStmt());
1107330f729Sjoerg 
1117330f729Sjoerg     SourceRange BodyRange = BodyCtx.getTopStmt()->getSourceRange();
1127330f729Sjoerg     const CapturedDiagList &DiagList = Pass.getDiags();
1137330f729Sjoerg     // Copy the diagnostics so we don't have to worry about invaliding iterators
1147330f729Sjoerg     // from the diagnostic list.
1157330f729Sjoerg     SmallVector<StoredDiagnostic, 16> StoredDiags;
1167330f729Sjoerg     StoredDiags.append(DiagList.begin(), DiagList.end());
1177330f729Sjoerg     SmallVectorImpl<StoredDiagnostic>::iterator
1187330f729Sjoerg         I = StoredDiags.begin(), E = StoredDiags.end();
1197330f729Sjoerg     while (I != E) {
1207330f729Sjoerg       if (I->getID() == diag::err_switch_into_protected_scope &&
1217330f729Sjoerg           isInRange(I->getLocation(), BodyRange)) {
1227330f729Sjoerg         handleProtectedScopeError(I, E);
1237330f729Sjoerg         continue;
1247330f729Sjoerg       }
1257330f729Sjoerg       ++I;
1267330f729Sjoerg     }
1277330f729Sjoerg   }
1287330f729Sjoerg 
handleProtectedScopeError(SmallVectorImpl<StoredDiagnostic>::iterator & DiagI,SmallVectorImpl<StoredDiagnostic>::iterator DiagE)1297330f729Sjoerg   void handleProtectedScopeError(
1307330f729Sjoerg                              SmallVectorImpl<StoredDiagnostic>::iterator &DiagI,
1317330f729Sjoerg                              SmallVectorImpl<StoredDiagnostic>::iterator DiagE){
1327330f729Sjoerg     Transaction Trans(Pass.TA);
1337330f729Sjoerg     assert(DiagI->getID() == diag::err_switch_into_protected_scope);
1347330f729Sjoerg     SourceLocation ErrLoc = DiagI->getLocation();
1357330f729Sjoerg     bool handledAllNotes = true;
1367330f729Sjoerg     ++DiagI;
1377330f729Sjoerg     for (; DiagI != DiagE && DiagI->getLevel() == DiagnosticsEngine::Note;
1387330f729Sjoerg          ++DiagI) {
1397330f729Sjoerg       if (!handleProtectedNote(*DiagI))
1407330f729Sjoerg         handledAllNotes = false;
1417330f729Sjoerg     }
1427330f729Sjoerg 
1437330f729Sjoerg     if (handledAllNotes)
1447330f729Sjoerg       Pass.TA.clearDiagnostic(diag::err_switch_into_protected_scope, ErrLoc);
1457330f729Sjoerg   }
1467330f729Sjoerg 
handleProtectedNote(const StoredDiagnostic & Diag)1477330f729Sjoerg   bool handleProtectedNote(const StoredDiagnostic &Diag) {
1487330f729Sjoerg     assert(Diag.getLevel() == DiagnosticsEngine::Note);
1497330f729Sjoerg 
1507330f729Sjoerg     for (unsigned i = 0; i != Cases.size(); i++) {
1517330f729Sjoerg       CaseInfo &info = Cases[i];
1527330f729Sjoerg       if (isInRange(Diag.getLocation(), info.Range)) {
1537330f729Sjoerg 
1547330f729Sjoerg         if (info.State == CaseInfo::St_Unchecked)
1557330f729Sjoerg           tryFixing(info);
1567330f729Sjoerg         assert(info.State != CaseInfo::St_Unchecked);
1577330f729Sjoerg 
1587330f729Sjoerg         if (info.State == CaseInfo::St_Fixed) {
1597330f729Sjoerg           Pass.TA.clearDiagnostic(Diag.getID(), Diag.getLocation());
1607330f729Sjoerg           return true;
1617330f729Sjoerg         }
1627330f729Sjoerg         return false;
1637330f729Sjoerg       }
1647330f729Sjoerg     }
1657330f729Sjoerg 
1667330f729Sjoerg     return false;
1677330f729Sjoerg   }
1687330f729Sjoerg 
tryFixing(CaseInfo & info)1697330f729Sjoerg   void tryFixing(CaseInfo &info) {
1707330f729Sjoerg     assert(info.State == CaseInfo::St_Unchecked);
1717330f729Sjoerg     if (hasVarReferencedOutside(info)) {
1727330f729Sjoerg       info.State = CaseInfo::St_CannotFix;
1737330f729Sjoerg       return;
1747330f729Sjoerg     }
1757330f729Sjoerg 
1767330f729Sjoerg     Pass.TA.insertAfterToken(info.SC->getColonLoc(), " {");
1777330f729Sjoerg     Pass.TA.insert(info.Range.getEnd(), "}\n");
1787330f729Sjoerg     info.State = CaseInfo::St_Fixed;
1797330f729Sjoerg   }
1807330f729Sjoerg 
hasVarReferencedOutside(CaseInfo & info)1817330f729Sjoerg   bool hasVarReferencedOutside(CaseInfo &info) {
1827330f729Sjoerg     for (unsigned i = 0, e = LocalRefs.size(); i != e; ++i) {
1837330f729Sjoerg       DeclRefExpr *DRE = LocalRefs[i];
1847330f729Sjoerg       if (isInRange(DRE->getDecl()->getLocation(), info.Range) &&
1857330f729Sjoerg           !isInRange(DRE->getLocation(), info.Range))
1867330f729Sjoerg         return true;
1877330f729Sjoerg     }
1887330f729Sjoerg     return false;
1897330f729Sjoerg   }
1907330f729Sjoerg 
isInRange(SourceLocation Loc,SourceRange R)1917330f729Sjoerg   bool isInRange(SourceLocation Loc, SourceRange R) {
1927330f729Sjoerg     if (Loc.isInvalid())
1937330f729Sjoerg       return false;
1947330f729Sjoerg     return !SM.isBeforeInTranslationUnit(Loc, R.getBegin()) &&
1957330f729Sjoerg             SM.isBeforeInTranslationUnit(Loc, R.getEnd());
1967330f729Sjoerg   }
1977330f729Sjoerg };
1987330f729Sjoerg 
1997330f729Sjoerg } // anonymous namespace
2007330f729Sjoerg 
traverseBody(BodyContext & BodyCtx)2017330f729Sjoerg void ProtectedScopeTraverser::traverseBody(BodyContext &BodyCtx) {
2027330f729Sjoerg   ProtectedScopeFixer Fix(BodyCtx);
2037330f729Sjoerg }
204