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: 300b57cec5SDimitry Andric LocalRefsCollector(SmallVectorImpl<DeclRefExpr *> &refs) 310b57cec5SDimitry Andric : Refs(refs) { } 320b57cec5SDimitry Andric 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 500b57cec5SDimitry Andric CaseInfo() : SC(nullptr), State(St_Unchecked) {} 510b57cec5SDimitry 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: 600b57cec5SDimitry Andric CaseCollector(ParentMap &PMap, SmallVectorImpl<CaseInfo> &Cases) 610b57cec5SDimitry Andric : PMap(PMap), Cases(Cases) { } 620b57cec5SDimitry Andric 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 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: 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 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 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 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 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 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 2010b57cec5SDimitry Andric void ProtectedScopeTraverser::traverseBody(BodyContext &BodyCtx) { 2020b57cec5SDimitry Andric ProtectedScopeFixer Fix(BodyCtx); 2030b57cec5SDimitry Andric } 204