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