|  | //===--- TransProtectedScope.cpp - Transformations to ARC mode ------------===// | 
|  | // | 
|  | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
|  | // See https://llvm.org/LICENSE.txt for license information. | 
|  | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  | // | 
|  | // Adds brackets in case statements that "contain" initialization of retaining | 
|  | // variable, thus emitting the "switch case is in protected scope" error. | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "Internals.h" | 
|  | #include "Transforms.h" | 
|  | #include "clang/AST/ASTContext.h" | 
|  | #include "clang/Basic/SourceManager.h" | 
|  | #include "clang/Sema/SemaDiagnostic.h" | 
|  |  | 
|  | using namespace clang; | 
|  | using namespace arcmt; | 
|  | using namespace trans; | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | class LocalRefsCollector : public RecursiveASTVisitor<LocalRefsCollector> { | 
|  | SmallVectorImpl<DeclRefExpr *> &Refs; | 
|  |  | 
|  | public: | 
|  | LocalRefsCollector(SmallVectorImpl<DeclRefExpr *> &refs) | 
|  | : Refs(refs) { } | 
|  |  | 
|  | bool VisitDeclRefExpr(DeclRefExpr *E) { | 
|  | if (ValueDecl *D = E->getDecl()) | 
|  | if (D->getDeclContext()->getRedeclContext()->isFunctionOrMethod()) | 
|  | Refs.push_back(E); | 
|  | return true; | 
|  | } | 
|  | }; | 
|  |  | 
|  | struct CaseInfo { | 
|  | SwitchCase *SC; | 
|  | SourceRange Range; | 
|  | enum { | 
|  | St_Unchecked, | 
|  | St_CannotFix, | 
|  | St_Fixed | 
|  | } State; | 
|  |  | 
|  | CaseInfo() : SC(nullptr), State(St_Unchecked) {} | 
|  | CaseInfo(SwitchCase *S, SourceRange Range) | 
|  | : SC(S), Range(Range), State(St_Unchecked) {} | 
|  | }; | 
|  |  | 
|  | class CaseCollector : public RecursiveASTVisitor<CaseCollector> { | 
|  | ParentMap &PMap; | 
|  | SmallVectorImpl<CaseInfo> &Cases; | 
|  |  | 
|  | public: | 
|  | CaseCollector(ParentMap &PMap, SmallVectorImpl<CaseInfo> &Cases) | 
|  | : PMap(PMap), Cases(Cases) { } | 
|  |  | 
|  | bool VisitSwitchStmt(SwitchStmt *S) { | 
|  | SwitchCase *Curr = S->getSwitchCaseList(); | 
|  | if (!Curr) | 
|  | return true; | 
|  | Stmt *Parent = getCaseParent(Curr); | 
|  | Curr = Curr->getNextSwitchCase(); | 
|  | // Make sure all case statements are in the same scope. | 
|  | while (Curr) { | 
|  | if (getCaseParent(Curr) != Parent) | 
|  | return true; | 
|  | Curr = Curr->getNextSwitchCase(); | 
|  | } | 
|  |  | 
|  | SourceLocation NextLoc = S->getEndLoc(); | 
|  | Curr = S->getSwitchCaseList(); | 
|  | // We iterate over case statements in reverse source-order. | 
|  | while (Curr) { | 
|  | Cases.push_back( | 
|  | CaseInfo(Curr, SourceRange(Curr->getBeginLoc(), NextLoc))); | 
|  | NextLoc = Curr->getBeginLoc(); | 
|  | Curr = Curr->getNextSwitchCase(); | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | Stmt *getCaseParent(SwitchCase *S) { | 
|  | Stmt *Parent = PMap.getParent(S); | 
|  | while (Parent && (isa<SwitchCase>(Parent) || isa<LabelStmt>(Parent))) | 
|  | Parent = PMap.getParent(Parent); | 
|  | return Parent; | 
|  | } | 
|  | }; | 
|  |  | 
|  | class ProtectedScopeFixer { | 
|  | MigrationPass &Pass; | 
|  | SourceManager &SM; | 
|  | SmallVector<CaseInfo, 16> Cases; | 
|  | SmallVector<DeclRefExpr *, 16> LocalRefs; | 
|  |  | 
|  | public: | 
|  | ProtectedScopeFixer(BodyContext &BodyCtx) | 
|  | : Pass(BodyCtx.getMigrationContext().Pass), | 
|  | SM(Pass.Ctx.getSourceManager()) { | 
|  |  | 
|  | CaseCollector(BodyCtx.getParentMap(), Cases) | 
|  | .TraverseStmt(BodyCtx.getTopStmt()); | 
|  | LocalRefsCollector(LocalRefs).TraverseStmt(BodyCtx.getTopStmt()); | 
|  |  | 
|  | SourceRange BodyRange = BodyCtx.getTopStmt()->getSourceRange(); | 
|  | const CapturedDiagList &DiagList = Pass.getDiags(); | 
|  | // Copy the diagnostics so we don't have to worry about invaliding iterators | 
|  | // from the diagnostic list. | 
|  | SmallVector<StoredDiagnostic, 16> StoredDiags; | 
|  | StoredDiags.append(DiagList.begin(), DiagList.end()); | 
|  | SmallVectorImpl<StoredDiagnostic>::iterator | 
|  | I = StoredDiags.begin(), E = StoredDiags.end(); | 
|  | while (I != E) { | 
|  | if (I->getID() == diag::err_switch_into_protected_scope && | 
|  | isInRange(I->getLocation(), BodyRange)) { | 
|  | handleProtectedScopeError(I, E); | 
|  | continue; | 
|  | } | 
|  | ++I; | 
|  | } | 
|  | } | 
|  |  | 
|  | void handleProtectedScopeError( | 
|  | SmallVectorImpl<StoredDiagnostic>::iterator &DiagI, | 
|  | SmallVectorImpl<StoredDiagnostic>::iterator DiagE){ | 
|  | Transaction Trans(Pass.TA); | 
|  | assert(DiagI->getID() == diag::err_switch_into_protected_scope); | 
|  | SourceLocation ErrLoc = DiagI->getLocation(); | 
|  | bool handledAllNotes = true; | 
|  | ++DiagI; | 
|  | for (; DiagI != DiagE && DiagI->getLevel() == DiagnosticsEngine::Note; | 
|  | ++DiagI) { | 
|  | if (!handleProtectedNote(*DiagI)) | 
|  | handledAllNotes = false; | 
|  | } | 
|  |  | 
|  | if (handledAllNotes) | 
|  | Pass.TA.clearDiagnostic(diag::err_switch_into_protected_scope, ErrLoc); | 
|  | } | 
|  |  | 
|  | bool handleProtectedNote(const StoredDiagnostic &Diag) { | 
|  | assert(Diag.getLevel() == DiagnosticsEngine::Note); | 
|  |  | 
|  | for (unsigned i = 0; i != Cases.size(); i++) { | 
|  | CaseInfo &info = Cases[i]; | 
|  | if (isInRange(Diag.getLocation(), info.Range)) { | 
|  |  | 
|  | if (info.State == CaseInfo::St_Unchecked) | 
|  | tryFixing(info); | 
|  | assert(info.State != CaseInfo::St_Unchecked); | 
|  |  | 
|  | if (info.State == CaseInfo::St_Fixed) { | 
|  | Pass.TA.clearDiagnostic(Diag.getID(), Diag.getLocation()); | 
|  | return true; | 
|  | } | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | void tryFixing(CaseInfo &info) { | 
|  | assert(info.State == CaseInfo::St_Unchecked); | 
|  | if (hasVarReferencedOutside(info)) { | 
|  | info.State = CaseInfo::St_CannotFix; | 
|  | return; | 
|  | } | 
|  |  | 
|  | Pass.TA.insertAfterToken(info.SC->getColonLoc(), " {"); | 
|  | Pass.TA.insert(info.Range.getEnd(), "}\n"); | 
|  | info.State = CaseInfo::St_Fixed; | 
|  | } | 
|  |  | 
|  | bool hasVarReferencedOutside(CaseInfo &info) { | 
|  | for (unsigned i = 0, e = LocalRefs.size(); i != e; ++i) { | 
|  | DeclRefExpr *DRE = LocalRefs[i]; | 
|  | if (isInRange(DRE->getDecl()->getLocation(), info.Range) && | 
|  | !isInRange(DRE->getLocation(), info.Range)) | 
|  | return true; | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool isInRange(SourceLocation Loc, SourceRange R) { | 
|  | if (Loc.isInvalid()) | 
|  | return false; | 
|  | return !SM.isBeforeInTranslationUnit(Loc, R.getBegin()) && | 
|  | SM.isBeforeInTranslationUnit(Loc, R.getEnd()); | 
|  | } | 
|  | }; | 
|  |  | 
|  | } // anonymous namespace | 
|  |  | 
|  | void ProtectedScopeTraverser::traverseBody(BodyContext &BodyCtx) { | 
|  | ProtectedScopeFixer Fix(BodyCtx); | 
|  | } |