Path: blob/main/contrib/llvm-project/clang/lib/ARCMigrate/TransProtectedScope.cpp
35236 views
//===--- TransProtectedScope.cpp - Transformations to ARC mode ------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// Adds brackets in case statements that "contain" initialization of retaining9// variable, thus emitting the "switch case is in protected scope" error.10//11//===----------------------------------------------------------------------===//1213#include "Internals.h"14#include "Transforms.h"15#include "clang/AST/ASTContext.h"16#include "clang/Basic/SourceManager.h"17#include "clang/Sema/SemaDiagnostic.h"1819using namespace clang;20using namespace arcmt;21using namespace trans;2223namespace {2425class LocalRefsCollector : public RecursiveASTVisitor<LocalRefsCollector> {26SmallVectorImpl<DeclRefExpr *> &Refs;2728public:29LocalRefsCollector(SmallVectorImpl<DeclRefExpr *> &refs)30: Refs(refs) { }3132bool VisitDeclRefExpr(DeclRefExpr *E) {33if (ValueDecl *D = E->getDecl())34if (D->getDeclContext()->getRedeclContext()->isFunctionOrMethod())35Refs.push_back(E);36return true;37}38};3940struct CaseInfo {41SwitchCase *SC;42SourceRange Range;43enum {44St_Unchecked,45St_CannotFix,46St_Fixed47} State;4849CaseInfo() : SC(nullptr), State(St_Unchecked) {}50CaseInfo(SwitchCase *S, SourceRange Range)51: SC(S), Range(Range), State(St_Unchecked) {}52};5354class CaseCollector : public RecursiveASTVisitor<CaseCollector> {55ParentMap &PMap;56SmallVectorImpl<CaseInfo> &Cases;5758public:59CaseCollector(ParentMap &PMap, SmallVectorImpl<CaseInfo> &Cases)60: PMap(PMap), Cases(Cases) { }6162bool VisitSwitchStmt(SwitchStmt *S) {63SwitchCase *Curr = S->getSwitchCaseList();64if (!Curr)65return true;66Stmt *Parent = getCaseParent(Curr);67Curr = Curr->getNextSwitchCase();68// Make sure all case statements are in the same scope.69while (Curr) {70if (getCaseParent(Curr) != Parent)71return true;72Curr = Curr->getNextSwitchCase();73}7475SourceLocation NextLoc = S->getEndLoc();76Curr = S->getSwitchCaseList();77// We iterate over case statements in reverse source-order.78while (Curr) {79Cases.push_back(80CaseInfo(Curr, SourceRange(Curr->getBeginLoc(), NextLoc)));81NextLoc = Curr->getBeginLoc();82Curr = Curr->getNextSwitchCase();83}84return true;85}8687Stmt *getCaseParent(SwitchCase *S) {88Stmt *Parent = PMap.getParent(S);89while (Parent && (isa<SwitchCase>(Parent) || isa<LabelStmt>(Parent)))90Parent = PMap.getParent(Parent);91return Parent;92}93};9495class ProtectedScopeFixer {96MigrationPass &Pass;97SourceManager &SM;98SmallVector<CaseInfo, 16> Cases;99SmallVector<DeclRefExpr *, 16> LocalRefs;100101public:102ProtectedScopeFixer(BodyContext &BodyCtx)103: Pass(BodyCtx.getMigrationContext().Pass),104SM(Pass.Ctx.getSourceManager()) {105106CaseCollector(BodyCtx.getParentMap(), Cases)107.TraverseStmt(BodyCtx.getTopStmt());108LocalRefsCollector(LocalRefs).TraverseStmt(BodyCtx.getTopStmt());109110SourceRange BodyRange = BodyCtx.getTopStmt()->getSourceRange();111const CapturedDiagList &DiagList = Pass.getDiags();112// Copy the diagnostics so we don't have to worry about invaliding iterators113// from the diagnostic list.114SmallVector<StoredDiagnostic, 16> StoredDiags;115StoredDiags.append(DiagList.begin(), DiagList.end());116SmallVectorImpl<StoredDiagnostic>::iterator117I = StoredDiags.begin(), E = StoredDiags.end();118while (I != E) {119if (I->getID() == diag::err_switch_into_protected_scope &&120isInRange(I->getLocation(), BodyRange)) {121handleProtectedScopeError(I, E);122continue;123}124++I;125}126}127128void handleProtectedScopeError(129SmallVectorImpl<StoredDiagnostic>::iterator &DiagI,130SmallVectorImpl<StoredDiagnostic>::iterator DiagE){131Transaction Trans(Pass.TA);132assert(DiagI->getID() == diag::err_switch_into_protected_scope);133SourceLocation ErrLoc = DiagI->getLocation();134bool handledAllNotes = true;135++DiagI;136for (; DiagI != DiagE && DiagI->getLevel() == DiagnosticsEngine::Note;137++DiagI) {138if (!handleProtectedNote(*DiagI))139handledAllNotes = false;140}141142if (handledAllNotes)143Pass.TA.clearDiagnostic(diag::err_switch_into_protected_scope, ErrLoc);144}145146bool handleProtectedNote(const StoredDiagnostic &Diag) {147assert(Diag.getLevel() == DiagnosticsEngine::Note);148149for (unsigned i = 0; i != Cases.size(); i++) {150CaseInfo &info = Cases[i];151if (isInRange(Diag.getLocation(), info.Range)) {152153if (info.State == CaseInfo::St_Unchecked)154tryFixing(info);155assert(info.State != CaseInfo::St_Unchecked);156157if (info.State == CaseInfo::St_Fixed) {158Pass.TA.clearDiagnostic(Diag.getID(), Diag.getLocation());159return true;160}161return false;162}163}164165return false;166}167168void tryFixing(CaseInfo &info) {169assert(info.State == CaseInfo::St_Unchecked);170if (hasVarReferencedOutside(info)) {171info.State = CaseInfo::St_CannotFix;172return;173}174175Pass.TA.insertAfterToken(info.SC->getColonLoc(), " {");176Pass.TA.insert(info.Range.getEnd(), "}\n");177info.State = CaseInfo::St_Fixed;178}179180bool hasVarReferencedOutside(CaseInfo &info) {181for (unsigned i = 0, e = LocalRefs.size(); i != e; ++i) {182DeclRefExpr *DRE = LocalRefs[i];183if (isInRange(DRE->getDecl()->getLocation(), info.Range) &&184!isInRange(DRE->getLocation(), info.Range))185return true;186}187return false;188}189190bool isInRange(SourceLocation Loc, SourceRange R) {191if (Loc.isInvalid())192return false;193return !SM.isBeforeInTranslationUnit(Loc, R.getBegin()) &&194SM.isBeforeInTranslationUnit(Loc, R.getEnd());195}196};197198} // anonymous namespace199200void ProtectedScopeTraverser::traverseBody(BodyContext &BodyCtx) {201ProtectedScopeFixer Fix(BodyCtx);202}203204205