103fbe3efSArgyrios Kyrtzidis //===--- TransProtectedScope.cpp - Transformations to ARC mode ------------===//
203fbe3efSArgyrios Kyrtzidis //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
603fbe3efSArgyrios Kyrtzidis //
703fbe3efSArgyrios Kyrtzidis //===----------------------------------------------------------------------===//
803fbe3efSArgyrios Kyrtzidis //
903fbe3efSArgyrios Kyrtzidis // Adds brackets in case statements that "contain" initialization of retaining
1003fbe3efSArgyrios Kyrtzidis // variable, thus emitting the "switch case is in protected scope" error.
1103fbe3efSArgyrios Kyrtzidis //
1203fbe3efSArgyrios Kyrtzidis //===----------------------------------------------------------------------===//
1303fbe3efSArgyrios Kyrtzidis
1403fbe3efSArgyrios Kyrtzidis #include "Internals.h"
15*86565c13SReid Kleckner #include "Transforms.h"
1608281fa3SArgyrios Kyrtzidis #include "clang/AST/ASTContext.h"
17*86565c13SReid Kleckner #include "clang/Basic/SourceManager.h"
184b41745eSChandler Carruth #include "clang/Sema/SemaDiagnostic.h"
1903fbe3efSArgyrios Kyrtzidis
2003fbe3efSArgyrios Kyrtzidis using namespace clang;
2103fbe3efSArgyrios Kyrtzidis using namespace arcmt;
2203fbe3efSArgyrios Kyrtzidis using namespace trans;
2303fbe3efSArgyrios Kyrtzidis
2403fbe3efSArgyrios Kyrtzidis namespace {
2503fbe3efSArgyrios Kyrtzidis
2608281fa3SArgyrios Kyrtzidis class LocalRefsCollector : public RecursiveASTVisitor<LocalRefsCollector> {
2708281fa3SArgyrios Kyrtzidis SmallVectorImpl<DeclRefExpr *> &Refs;
2808281fa3SArgyrios Kyrtzidis
2908281fa3SArgyrios Kyrtzidis public:
LocalRefsCollector(SmallVectorImpl<DeclRefExpr * > & refs)3008281fa3SArgyrios Kyrtzidis LocalRefsCollector(SmallVectorImpl<DeclRefExpr *> &refs)
3108281fa3SArgyrios Kyrtzidis : Refs(refs) { }
3208281fa3SArgyrios Kyrtzidis
VisitDeclRefExpr(DeclRefExpr * E)3308281fa3SArgyrios Kyrtzidis bool VisitDeclRefExpr(DeclRefExpr *E) {
3408281fa3SArgyrios Kyrtzidis if (ValueDecl *D = E->getDecl())
3508281fa3SArgyrios Kyrtzidis if (D->getDeclContext()->getRedeclContext()->isFunctionOrMethod())
3608281fa3SArgyrios Kyrtzidis Refs.push_back(E);
3708281fa3SArgyrios Kyrtzidis return true;
3808281fa3SArgyrios Kyrtzidis }
3908281fa3SArgyrios Kyrtzidis };
4008281fa3SArgyrios Kyrtzidis
4103fbe3efSArgyrios Kyrtzidis struct CaseInfo {
4203fbe3efSArgyrios Kyrtzidis SwitchCase *SC;
4303fbe3efSArgyrios Kyrtzidis SourceRange Range;
4408281fa3SArgyrios Kyrtzidis enum {
4508281fa3SArgyrios Kyrtzidis St_Unchecked,
4608281fa3SArgyrios Kyrtzidis St_CannotFix,
4708281fa3SArgyrios Kyrtzidis St_Fixed
4808281fa3SArgyrios Kyrtzidis } State;
4903fbe3efSArgyrios Kyrtzidis
CaseInfo__anonf9ad4e870111::CaseInfo508ae12039SCraig Topper CaseInfo() : SC(nullptr), State(St_Unchecked) {}
CaseInfo__anonf9ad4e870111::CaseInfo5103fbe3efSArgyrios Kyrtzidis CaseInfo(SwitchCase *S, SourceRange Range)
5208281fa3SArgyrios Kyrtzidis : SC(S), Range(Range), State(St_Unchecked) {}
5303fbe3efSArgyrios Kyrtzidis };
5403fbe3efSArgyrios Kyrtzidis
5503fbe3efSArgyrios Kyrtzidis class CaseCollector : public RecursiveASTVisitor<CaseCollector> {
5608281fa3SArgyrios Kyrtzidis ParentMap &PMap;
57f857950dSDmitri Gribenko SmallVectorImpl<CaseInfo> &Cases;
5803fbe3efSArgyrios Kyrtzidis
5903fbe3efSArgyrios Kyrtzidis public:
CaseCollector(ParentMap & PMap,SmallVectorImpl<CaseInfo> & Cases)60f857950dSDmitri Gribenko CaseCollector(ParentMap &PMap, SmallVectorImpl<CaseInfo> &Cases)
6108281fa3SArgyrios Kyrtzidis : PMap(PMap), Cases(Cases) { }
6203fbe3efSArgyrios Kyrtzidis
VisitSwitchStmt(SwitchStmt * S)6303fbe3efSArgyrios Kyrtzidis bool VisitSwitchStmt(SwitchStmt *S) {
6403fbe3efSArgyrios Kyrtzidis SwitchCase *Curr = S->getSwitchCaseList();
6508281fa3SArgyrios Kyrtzidis if (!Curr)
6608281fa3SArgyrios Kyrtzidis return true;
6708281fa3SArgyrios Kyrtzidis Stmt *Parent = getCaseParent(Curr);
6808281fa3SArgyrios Kyrtzidis Curr = Curr->getNextSwitchCase();
6908281fa3SArgyrios Kyrtzidis // Make sure all case statements are in the same scope.
7008281fa3SArgyrios Kyrtzidis while (Curr) {
7108281fa3SArgyrios Kyrtzidis if (getCaseParent(Curr) != Parent)
7208281fa3SArgyrios Kyrtzidis return true;
7308281fa3SArgyrios Kyrtzidis Curr = Curr->getNextSwitchCase();
7408281fa3SArgyrios Kyrtzidis }
7508281fa3SArgyrios Kyrtzidis
761c301dcbSStephen Kelly SourceLocation NextLoc = S->getEndLoc();
7708281fa3SArgyrios Kyrtzidis Curr = S->getSwitchCaseList();
7803fbe3efSArgyrios Kyrtzidis // We iterate over case statements in reverse source-order.
7903fbe3efSArgyrios Kyrtzidis while (Curr) {
80f2ceec48SStephen Kelly Cases.push_back(
81f2ceec48SStephen Kelly CaseInfo(Curr, SourceRange(Curr->getBeginLoc(), NextLoc)));
82f2ceec48SStephen Kelly NextLoc = Curr->getBeginLoc();
8303fbe3efSArgyrios Kyrtzidis Curr = Curr->getNextSwitchCase();
8403fbe3efSArgyrios Kyrtzidis }
8503fbe3efSArgyrios Kyrtzidis return true;
8603fbe3efSArgyrios Kyrtzidis }
8708281fa3SArgyrios Kyrtzidis
getCaseParent(SwitchCase * S)8808281fa3SArgyrios Kyrtzidis Stmt *getCaseParent(SwitchCase *S) {
8908281fa3SArgyrios Kyrtzidis Stmt *Parent = PMap.getParent(S);
9008281fa3SArgyrios Kyrtzidis while (Parent && (isa<SwitchCase>(Parent) || isa<LabelStmt>(Parent)))
9108281fa3SArgyrios Kyrtzidis Parent = PMap.getParent(Parent);
9208281fa3SArgyrios Kyrtzidis return Parent;
9308281fa3SArgyrios Kyrtzidis }
9403fbe3efSArgyrios Kyrtzidis };
9503fbe3efSArgyrios Kyrtzidis
9608281fa3SArgyrios Kyrtzidis class ProtectedScopeFixer {
9708281fa3SArgyrios Kyrtzidis MigrationPass &Pass;
9808281fa3SArgyrios Kyrtzidis SourceManager &SM;
9903fbe3efSArgyrios Kyrtzidis SmallVector<CaseInfo, 16> Cases;
10008281fa3SArgyrios Kyrtzidis SmallVector<DeclRefExpr *, 16> LocalRefs;
10108281fa3SArgyrios Kyrtzidis
10208281fa3SArgyrios Kyrtzidis public:
ProtectedScopeFixer(BodyContext & BodyCtx)10308281fa3SArgyrios Kyrtzidis ProtectedScopeFixer(BodyContext &BodyCtx)
10408281fa3SArgyrios Kyrtzidis : Pass(BodyCtx.getMigrationContext().Pass),
10508281fa3SArgyrios Kyrtzidis SM(Pass.Ctx.getSourceManager()) {
10608281fa3SArgyrios Kyrtzidis
10708281fa3SArgyrios Kyrtzidis CaseCollector(BodyCtx.getParentMap(), Cases)
10808281fa3SArgyrios Kyrtzidis .TraverseStmt(BodyCtx.getTopStmt());
10908281fa3SArgyrios Kyrtzidis LocalRefsCollector(LocalRefs).TraverseStmt(BodyCtx.getTopStmt());
11003fbe3efSArgyrios Kyrtzidis
11103fbe3efSArgyrios Kyrtzidis SourceRange BodyRange = BodyCtx.getTopStmt()->getSourceRange();
11203fbe3efSArgyrios Kyrtzidis const CapturedDiagList &DiagList = Pass.getDiags();
113d2c0abadSArgyrios Kyrtzidis // Copy the diagnostics so we don't have to worry about invaliding iterators
114d2c0abadSArgyrios Kyrtzidis // from the diagnostic list.
115d2c0abadSArgyrios Kyrtzidis SmallVector<StoredDiagnostic, 16> StoredDiags;
116d2c0abadSArgyrios Kyrtzidis StoredDiags.append(DiagList.begin(), DiagList.end());
117d2c0abadSArgyrios Kyrtzidis SmallVectorImpl<StoredDiagnostic>::iterator
118d2c0abadSArgyrios Kyrtzidis I = StoredDiags.begin(), E = StoredDiags.end();
11903fbe3efSArgyrios Kyrtzidis while (I != E) {
12003fbe3efSArgyrios Kyrtzidis if (I->getID() == diag::err_switch_into_protected_scope &&
12103fbe3efSArgyrios Kyrtzidis isInRange(I->getLocation(), BodyRange)) {
12208281fa3SArgyrios Kyrtzidis handleProtectedScopeError(I, E);
12303fbe3efSArgyrios Kyrtzidis continue;
12403fbe3efSArgyrios Kyrtzidis }
12503fbe3efSArgyrios Kyrtzidis ++I;
12603fbe3efSArgyrios Kyrtzidis }
12703fbe3efSArgyrios Kyrtzidis }
12808281fa3SArgyrios Kyrtzidis
handleProtectedScopeError(SmallVectorImpl<StoredDiagnostic>::iterator & DiagI,SmallVectorImpl<StoredDiagnostic>::iterator DiagE)129d2c0abadSArgyrios Kyrtzidis void handleProtectedScopeError(
130d2c0abadSArgyrios Kyrtzidis SmallVectorImpl<StoredDiagnostic>::iterator &DiagI,
131d2c0abadSArgyrios Kyrtzidis SmallVectorImpl<StoredDiagnostic>::iterator DiagE){
13208281fa3SArgyrios Kyrtzidis Transaction Trans(Pass.TA);
13308281fa3SArgyrios Kyrtzidis assert(DiagI->getID() == diag::err_switch_into_protected_scope);
13408281fa3SArgyrios Kyrtzidis SourceLocation ErrLoc = DiagI->getLocation();
13508281fa3SArgyrios Kyrtzidis bool handledAllNotes = true;
13608281fa3SArgyrios Kyrtzidis ++DiagI;
13708281fa3SArgyrios Kyrtzidis for (; DiagI != DiagE && DiagI->getLevel() == DiagnosticsEngine::Note;
13808281fa3SArgyrios Kyrtzidis ++DiagI) {
13908281fa3SArgyrios Kyrtzidis if (!handleProtectedNote(*DiagI))
14008281fa3SArgyrios Kyrtzidis handledAllNotes = false;
14108281fa3SArgyrios Kyrtzidis }
14208281fa3SArgyrios Kyrtzidis
14308281fa3SArgyrios Kyrtzidis if (handledAllNotes)
14408281fa3SArgyrios Kyrtzidis Pass.TA.clearDiagnostic(diag::err_switch_into_protected_scope, ErrLoc);
14508281fa3SArgyrios Kyrtzidis }
14608281fa3SArgyrios Kyrtzidis
handleProtectedNote(const StoredDiagnostic & Diag)14708281fa3SArgyrios Kyrtzidis bool handleProtectedNote(const StoredDiagnostic &Diag) {
14808281fa3SArgyrios Kyrtzidis assert(Diag.getLevel() == DiagnosticsEngine::Note);
14908281fa3SArgyrios Kyrtzidis
15008281fa3SArgyrios Kyrtzidis for (unsigned i = 0; i != Cases.size(); i++) {
15108281fa3SArgyrios Kyrtzidis CaseInfo &info = Cases[i];
15208281fa3SArgyrios Kyrtzidis if (isInRange(Diag.getLocation(), info.Range)) {
15308281fa3SArgyrios Kyrtzidis
15408281fa3SArgyrios Kyrtzidis if (info.State == CaseInfo::St_Unchecked)
15508281fa3SArgyrios Kyrtzidis tryFixing(info);
15608281fa3SArgyrios Kyrtzidis assert(info.State != CaseInfo::St_Unchecked);
15708281fa3SArgyrios Kyrtzidis
15808281fa3SArgyrios Kyrtzidis if (info.State == CaseInfo::St_Fixed) {
15908281fa3SArgyrios Kyrtzidis Pass.TA.clearDiagnostic(Diag.getID(), Diag.getLocation());
16008281fa3SArgyrios Kyrtzidis return true;
16108281fa3SArgyrios Kyrtzidis }
16208281fa3SArgyrios Kyrtzidis return false;
16308281fa3SArgyrios Kyrtzidis }
16408281fa3SArgyrios Kyrtzidis }
16508281fa3SArgyrios Kyrtzidis
16608281fa3SArgyrios Kyrtzidis return false;
16708281fa3SArgyrios Kyrtzidis }
16808281fa3SArgyrios Kyrtzidis
tryFixing(CaseInfo & info)16908281fa3SArgyrios Kyrtzidis void tryFixing(CaseInfo &info) {
17008281fa3SArgyrios Kyrtzidis assert(info.State == CaseInfo::St_Unchecked);
17108281fa3SArgyrios Kyrtzidis if (hasVarReferencedOutside(info)) {
17208281fa3SArgyrios Kyrtzidis info.State = CaseInfo::St_CannotFix;
17308281fa3SArgyrios Kyrtzidis return;
17408281fa3SArgyrios Kyrtzidis }
17508281fa3SArgyrios Kyrtzidis
17608281fa3SArgyrios Kyrtzidis Pass.TA.insertAfterToken(info.SC->getColonLoc(), " {");
17708281fa3SArgyrios Kyrtzidis Pass.TA.insert(info.Range.getEnd(), "}\n");
17808281fa3SArgyrios Kyrtzidis info.State = CaseInfo::St_Fixed;
17908281fa3SArgyrios Kyrtzidis }
18008281fa3SArgyrios Kyrtzidis
hasVarReferencedOutside(CaseInfo & info)18108281fa3SArgyrios Kyrtzidis bool hasVarReferencedOutside(CaseInfo &info) {
18208281fa3SArgyrios Kyrtzidis for (unsigned i = 0, e = LocalRefs.size(); i != e; ++i) {
18308281fa3SArgyrios Kyrtzidis DeclRefExpr *DRE = LocalRefs[i];
18408281fa3SArgyrios Kyrtzidis if (isInRange(DRE->getDecl()->getLocation(), info.Range) &&
18508281fa3SArgyrios Kyrtzidis !isInRange(DRE->getLocation(), info.Range))
18608281fa3SArgyrios Kyrtzidis return true;
18708281fa3SArgyrios Kyrtzidis }
18808281fa3SArgyrios Kyrtzidis return false;
18908281fa3SArgyrios Kyrtzidis }
19008281fa3SArgyrios Kyrtzidis
isInRange(SourceLocation Loc,SourceRange R)19108281fa3SArgyrios Kyrtzidis bool isInRange(SourceLocation Loc, SourceRange R) {
19208281fa3SArgyrios Kyrtzidis if (Loc.isInvalid())
19308281fa3SArgyrios Kyrtzidis return false;
19408281fa3SArgyrios Kyrtzidis return !SM.isBeforeInTranslationUnit(Loc, R.getBegin()) &&
19508281fa3SArgyrios Kyrtzidis SM.isBeforeInTranslationUnit(Loc, R.getEnd());
19608281fa3SArgyrios Kyrtzidis }
19708281fa3SArgyrios Kyrtzidis };
19808281fa3SArgyrios Kyrtzidis
19908281fa3SArgyrios Kyrtzidis } // anonymous namespace
20008281fa3SArgyrios Kyrtzidis
traverseBody(BodyContext & BodyCtx)20108281fa3SArgyrios Kyrtzidis void ProtectedScopeTraverser::traverseBody(BodyContext &BodyCtx) {
20208281fa3SArgyrios Kyrtzidis ProtectedScopeFixer Fix(BodyCtx);
20308281fa3SArgyrios Kyrtzidis }
204