1*0b57cec5SDimitry Andric //===--- TransProtectedScope.cpp - Transformations to ARC mode ------------===// 2*0b57cec5SDimitry Andric // 3*0b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*0b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*0b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*0b57cec5SDimitry Andric // 7*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 8*0b57cec5SDimitry Andric // 9*0b57cec5SDimitry Andric // Adds brackets in case statements that "contain" initialization of retaining 10*0b57cec5SDimitry Andric // variable, thus emitting the "switch case is in protected scope" error. 11*0b57cec5SDimitry Andric // 12*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 13*0b57cec5SDimitry Andric 14*0b57cec5SDimitry Andric #include "Transforms.h" 15*0b57cec5SDimitry Andric #include "Internals.h" 16*0b57cec5SDimitry Andric #include "clang/AST/ASTContext.h" 17*0b57cec5SDimitry Andric #include "clang/Sema/SemaDiagnostic.h" 18*0b57cec5SDimitry Andric 19*0b57cec5SDimitry Andric using namespace clang; 20*0b57cec5SDimitry Andric using namespace arcmt; 21*0b57cec5SDimitry Andric using namespace trans; 22*0b57cec5SDimitry Andric 23*0b57cec5SDimitry Andric namespace { 24*0b57cec5SDimitry Andric 25*0b57cec5SDimitry Andric class LocalRefsCollector : public RecursiveASTVisitor<LocalRefsCollector> { 26*0b57cec5SDimitry Andric SmallVectorImpl<DeclRefExpr *> &Refs; 27*0b57cec5SDimitry Andric 28*0b57cec5SDimitry Andric public: 29*0b57cec5SDimitry Andric LocalRefsCollector(SmallVectorImpl<DeclRefExpr *> &refs) 30*0b57cec5SDimitry Andric : Refs(refs) { } 31*0b57cec5SDimitry Andric 32*0b57cec5SDimitry Andric bool VisitDeclRefExpr(DeclRefExpr *E) { 33*0b57cec5SDimitry Andric if (ValueDecl *D = E->getDecl()) 34*0b57cec5SDimitry Andric if (D->getDeclContext()->getRedeclContext()->isFunctionOrMethod()) 35*0b57cec5SDimitry Andric Refs.push_back(E); 36*0b57cec5SDimitry Andric return true; 37*0b57cec5SDimitry Andric } 38*0b57cec5SDimitry Andric }; 39*0b57cec5SDimitry Andric 40*0b57cec5SDimitry Andric struct CaseInfo { 41*0b57cec5SDimitry Andric SwitchCase *SC; 42*0b57cec5SDimitry Andric SourceRange Range; 43*0b57cec5SDimitry Andric enum { 44*0b57cec5SDimitry Andric St_Unchecked, 45*0b57cec5SDimitry Andric St_CannotFix, 46*0b57cec5SDimitry Andric St_Fixed 47*0b57cec5SDimitry Andric } State; 48*0b57cec5SDimitry Andric 49*0b57cec5SDimitry Andric CaseInfo() : SC(nullptr), State(St_Unchecked) {} 50*0b57cec5SDimitry Andric CaseInfo(SwitchCase *S, SourceRange Range) 51*0b57cec5SDimitry Andric : SC(S), Range(Range), State(St_Unchecked) {} 52*0b57cec5SDimitry Andric }; 53*0b57cec5SDimitry Andric 54*0b57cec5SDimitry Andric class CaseCollector : public RecursiveASTVisitor<CaseCollector> { 55*0b57cec5SDimitry Andric ParentMap &PMap; 56*0b57cec5SDimitry Andric SmallVectorImpl<CaseInfo> &Cases; 57*0b57cec5SDimitry Andric 58*0b57cec5SDimitry Andric public: 59*0b57cec5SDimitry Andric CaseCollector(ParentMap &PMap, SmallVectorImpl<CaseInfo> &Cases) 60*0b57cec5SDimitry Andric : PMap(PMap), Cases(Cases) { } 61*0b57cec5SDimitry Andric 62*0b57cec5SDimitry Andric bool VisitSwitchStmt(SwitchStmt *S) { 63*0b57cec5SDimitry Andric SwitchCase *Curr = S->getSwitchCaseList(); 64*0b57cec5SDimitry Andric if (!Curr) 65*0b57cec5SDimitry Andric return true; 66*0b57cec5SDimitry Andric Stmt *Parent = getCaseParent(Curr); 67*0b57cec5SDimitry Andric Curr = Curr->getNextSwitchCase(); 68*0b57cec5SDimitry Andric // Make sure all case statements are in the same scope. 69*0b57cec5SDimitry Andric while (Curr) { 70*0b57cec5SDimitry Andric if (getCaseParent(Curr) != Parent) 71*0b57cec5SDimitry Andric return true; 72*0b57cec5SDimitry Andric Curr = Curr->getNextSwitchCase(); 73*0b57cec5SDimitry Andric } 74*0b57cec5SDimitry Andric 75*0b57cec5SDimitry Andric SourceLocation NextLoc = S->getEndLoc(); 76*0b57cec5SDimitry Andric Curr = S->getSwitchCaseList(); 77*0b57cec5SDimitry Andric // We iterate over case statements in reverse source-order. 78*0b57cec5SDimitry Andric while (Curr) { 79*0b57cec5SDimitry Andric Cases.push_back( 80*0b57cec5SDimitry Andric CaseInfo(Curr, SourceRange(Curr->getBeginLoc(), NextLoc))); 81*0b57cec5SDimitry Andric NextLoc = Curr->getBeginLoc(); 82*0b57cec5SDimitry Andric Curr = Curr->getNextSwitchCase(); 83*0b57cec5SDimitry Andric } 84*0b57cec5SDimitry Andric return true; 85*0b57cec5SDimitry Andric } 86*0b57cec5SDimitry Andric 87*0b57cec5SDimitry Andric Stmt *getCaseParent(SwitchCase *S) { 88*0b57cec5SDimitry Andric Stmt *Parent = PMap.getParent(S); 89*0b57cec5SDimitry Andric while (Parent && (isa<SwitchCase>(Parent) || isa<LabelStmt>(Parent))) 90*0b57cec5SDimitry Andric Parent = PMap.getParent(Parent); 91*0b57cec5SDimitry Andric return Parent; 92*0b57cec5SDimitry Andric } 93*0b57cec5SDimitry Andric }; 94*0b57cec5SDimitry Andric 95*0b57cec5SDimitry Andric class ProtectedScopeFixer { 96*0b57cec5SDimitry Andric MigrationPass &Pass; 97*0b57cec5SDimitry Andric SourceManager &SM; 98*0b57cec5SDimitry Andric SmallVector<CaseInfo, 16> Cases; 99*0b57cec5SDimitry Andric SmallVector<DeclRefExpr *, 16> LocalRefs; 100*0b57cec5SDimitry Andric 101*0b57cec5SDimitry Andric public: 102*0b57cec5SDimitry Andric ProtectedScopeFixer(BodyContext &BodyCtx) 103*0b57cec5SDimitry Andric : Pass(BodyCtx.getMigrationContext().Pass), 104*0b57cec5SDimitry Andric SM(Pass.Ctx.getSourceManager()) { 105*0b57cec5SDimitry Andric 106*0b57cec5SDimitry Andric CaseCollector(BodyCtx.getParentMap(), Cases) 107*0b57cec5SDimitry Andric .TraverseStmt(BodyCtx.getTopStmt()); 108*0b57cec5SDimitry Andric LocalRefsCollector(LocalRefs).TraverseStmt(BodyCtx.getTopStmt()); 109*0b57cec5SDimitry Andric 110*0b57cec5SDimitry Andric SourceRange BodyRange = BodyCtx.getTopStmt()->getSourceRange(); 111*0b57cec5SDimitry Andric const CapturedDiagList &DiagList = Pass.getDiags(); 112*0b57cec5SDimitry Andric // Copy the diagnostics so we don't have to worry about invaliding iterators 113*0b57cec5SDimitry Andric // from the diagnostic list. 114*0b57cec5SDimitry Andric SmallVector<StoredDiagnostic, 16> StoredDiags; 115*0b57cec5SDimitry Andric StoredDiags.append(DiagList.begin(), DiagList.end()); 116*0b57cec5SDimitry Andric SmallVectorImpl<StoredDiagnostic>::iterator 117*0b57cec5SDimitry Andric I = StoredDiags.begin(), E = StoredDiags.end(); 118*0b57cec5SDimitry Andric while (I != E) { 119*0b57cec5SDimitry Andric if (I->getID() == diag::err_switch_into_protected_scope && 120*0b57cec5SDimitry Andric isInRange(I->getLocation(), BodyRange)) { 121*0b57cec5SDimitry Andric handleProtectedScopeError(I, E); 122*0b57cec5SDimitry Andric continue; 123*0b57cec5SDimitry Andric } 124*0b57cec5SDimitry Andric ++I; 125*0b57cec5SDimitry Andric } 126*0b57cec5SDimitry Andric } 127*0b57cec5SDimitry Andric 128*0b57cec5SDimitry Andric void handleProtectedScopeError( 129*0b57cec5SDimitry Andric SmallVectorImpl<StoredDiagnostic>::iterator &DiagI, 130*0b57cec5SDimitry Andric SmallVectorImpl<StoredDiagnostic>::iterator DiagE){ 131*0b57cec5SDimitry Andric Transaction Trans(Pass.TA); 132*0b57cec5SDimitry Andric assert(DiagI->getID() == diag::err_switch_into_protected_scope); 133*0b57cec5SDimitry Andric SourceLocation ErrLoc = DiagI->getLocation(); 134*0b57cec5SDimitry Andric bool handledAllNotes = true; 135*0b57cec5SDimitry Andric ++DiagI; 136*0b57cec5SDimitry Andric for (; DiagI != DiagE && DiagI->getLevel() == DiagnosticsEngine::Note; 137*0b57cec5SDimitry Andric ++DiagI) { 138*0b57cec5SDimitry Andric if (!handleProtectedNote(*DiagI)) 139*0b57cec5SDimitry Andric handledAllNotes = false; 140*0b57cec5SDimitry Andric } 141*0b57cec5SDimitry Andric 142*0b57cec5SDimitry Andric if (handledAllNotes) 143*0b57cec5SDimitry Andric Pass.TA.clearDiagnostic(diag::err_switch_into_protected_scope, ErrLoc); 144*0b57cec5SDimitry Andric } 145*0b57cec5SDimitry Andric 146*0b57cec5SDimitry Andric bool handleProtectedNote(const StoredDiagnostic &Diag) { 147*0b57cec5SDimitry Andric assert(Diag.getLevel() == DiagnosticsEngine::Note); 148*0b57cec5SDimitry Andric 149*0b57cec5SDimitry Andric for (unsigned i = 0; i != Cases.size(); i++) { 150*0b57cec5SDimitry Andric CaseInfo &info = Cases[i]; 151*0b57cec5SDimitry Andric if (isInRange(Diag.getLocation(), info.Range)) { 152*0b57cec5SDimitry Andric 153*0b57cec5SDimitry Andric if (info.State == CaseInfo::St_Unchecked) 154*0b57cec5SDimitry Andric tryFixing(info); 155*0b57cec5SDimitry Andric assert(info.State != CaseInfo::St_Unchecked); 156*0b57cec5SDimitry Andric 157*0b57cec5SDimitry Andric if (info.State == CaseInfo::St_Fixed) { 158*0b57cec5SDimitry Andric Pass.TA.clearDiagnostic(Diag.getID(), Diag.getLocation()); 159*0b57cec5SDimitry Andric return true; 160*0b57cec5SDimitry Andric } 161*0b57cec5SDimitry Andric return false; 162*0b57cec5SDimitry Andric } 163*0b57cec5SDimitry Andric } 164*0b57cec5SDimitry Andric 165*0b57cec5SDimitry Andric return false; 166*0b57cec5SDimitry Andric } 167*0b57cec5SDimitry Andric 168*0b57cec5SDimitry Andric void tryFixing(CaseInfo &info) { 169*0b57cec5SDimitry Andric assert(info.State == CaseInfo::St_Unchecked); 170*0b57cec5SDimitry Andric if (hasVarReferencedOutside(info)) { 171*0b57cec5SDimitry Andric info.State = CaseInfo::St_CannotFix; 172*0b57cec5SDimitry Andric return; 173*0b57cec5SDimitry Andric } 174*0b57cec5SDimitry Andric 175*0b57cec5SDimitry Andric Pass.TA.insertAfterToken(info.SC->getColonLoc(), " {"); 176*0b57cec5SDimitry Andric Pass.TA.insert(info.Range.getEnd(), "}\n"); 177*0b57cec5SDimitry Andric info.State = CaseInfo::St_Fixed; 178*0b57cec5SDimitry Andric } 179*0b57cec5SDimitry Andric 180*0b57cec5SDimitry Andric bool hasVarReferencedOutside(CaseInfo &info) { 181*0b57cec5SDimitry Andric for (unsigned i = 0, e = LocalRefs.size(); i != e; ++i) { 182*0b57cec5SDimitry Andric DeclRefExpr *DRE = LocalRefs[i]; 183*0b57cec5SDimitry Andric if (isInRange(DRE->getDecl()->getLocation(), info.Range) && 184*0b57cec5SDimitry Andric !isInRange(DRE->getLocation(), info.Range)) 185*0b57cec5SDimitry Andric return true; 186*0b57cec5SDimitry Andric } 187*0b57cec5SDimitry Andric return false; 188*0b57cec5SDimitry Andric } 189*0b57cec5SDimitry Andric 190*0b57cec5SDimitry Andric bool isInRange(SourceLocation Loc, SourceRange R) { 191*0b57cec5SDimitry Andric if (Loc.isInvalid()) 192*0b57cec5SDimitry Andric return false; 193*0b57cec5SDimitry Andric return !SM.isBeforeInTranslationUnit(Loc, R.getBegin()) && 194*0b57cec5SDimitry Andric SM.isBeforeInTranslationUnit(Loc, R.getEnd()); 195*0b57cec5SDimitry Andric } 196*0b57cec5SDimitry Andric }; 197*0b57cec5SDimitry Andric 198*0b57cec5SDimitry Andric } // anonymous namespace 199*0b57cec5SDimitry Andric 200*0b57cec5SDimitry Andric void ProtectedScopeTraverser::traverseBody(BodyContext &BodyCtx) { 201*0b57cec5SDimitry Andric ProtectedScopeFixer Fix(BodyCtx); 202*0b57cec5SDimitry Andric } 203