clang API Documentation

TransProtectedScope.cpp
Go to the documentation of this file.
00001 //===--- TransProtectedScope.cpp - Transformations to ARC mode ------------===//
00002 //
00003 //                     The LLVM Compiler Infrastructure
00004 //
00005 // This file is distributed under the University of Illinois Open Source
00006 // License. See LICENSE.TXT for details.
00007 //
00008 //===----------------------------------------------------------------------===//
00009 //
00010 // Adds brackets in case statements that "contain" initialization of retaining
00011 // variable, thus emitting the "switch case is in protected scope" error.
00012 //
00013 //===----------------------------------------------------------------------===//
00014 
00015 #include "Transforms.h"
00016 #include "Internals.h"
00017 #include "clang/AST/ASTContext.h"
00018 #include "clang/Sema/SemaDiagnostic.h"
00019 
00020 using namespace clang;
00021 using namespace arcmt;
00022 using namespace trans;
00023 
00024 namespace {
00025 
00026 class LocalRefsCollector : public RecursiveASTVisitor<LocalRefsCollector> {
00027   SmallVectorImpl<DeclRefExpr *> &Refs;
00028 
00029 public:
00030   LocalRefsCollector(SmallVectorImpl<DeclRefExpr *> &refs)
00031     : Refs(refs) { }
00032 
00033   bool VisitDeclRefExpr(DeclRefExpr *E) {
00034     if (ValueDecl *D = E->getDecl())
00035       if (D->getDeclContext()->getRedeclContext()->isFunctionOrMethod())
00036         Refs.push_back(E);
00037     return true;
00038   }
00039 };
00040 
00041 struct CaseInfo {
00042   SwitchCase *SC;
00043   SourceRange Range;
00044   enum {
00045     St_Unchecked,
00046     St_CannotFix,
00047     St_Fixed
00048   } State;
00049   
00050   CaseInfo() : SC(nullptr), State(St_Unchecked) {}
00051   CaseInfo(SwitchCase *S, SourceRange Range)
00052     : SC(S), Range(Range), State(St_Unchecked) {}
00053 };
00054 
00055 class CaseCollector : public RecursiveASTVisitor<CaseCollector> {
00056   ParentMap &PMap;
00057   SmallVectorImpl<CaseInfo> &Cases;
00058 
00059 public:
00060   CaseCollector(ParentMap &PMap, SmallVectorImpl<CaseInfo> &Cases)
00061     : PMap(PMap), Cases(Cases) { }
00062 
00063   bool VisitSwitchStmt(SwitchStmt *S) {
00064     SwitchCase *Curr = S->getSwitchCaseList();
00065     if (!Curr)
00066       return true;
00067     Stmt *Parent = getCaseParent(Curr);
00068     Curr = Curr->getNextSwitchCase();
00069     // Make sure all case statements are in the same scope.
00070     while (Curr) {
00071       if (getCaseParent(Curr) != Parent)
00072         return true;
00073       Curr = Curr->getNextSwitchCase();
00074     }
00075 
00076     SourceLocation NextLoc = S->getLocEnd();
00077     Curr = S->getSwitchCaseList();
00078     // We iterate over case statements in reverse source-order.
00079     while (Curr) {
00080       Cases.push_back(CaseInfo(Curr,SourceRange(Curr->getLocStart(), NextLoc)));
00081       NextLoc = Curr->getLocStart();
00082       Curr = Curr->getNextSwitchCase();
00083     }
00084     return true;
00085   }
00086 
00087   Stmt *getCaseParent(SwitchCase *S) {
00088     Stmt *Parent = PMap.getParent(S);
00089     while (Parent && (isa<SwitchCase>(Parent) || isa<LabelStmt>(Parent)))
00090       Parent = PMap.getParent(Parent);
00091     return Parent;
00092   }
00093 };
00094 
00095 class ProtectedScopeFixer {
00096   MigrationPass &Pass;
00097   SourceManager &SM;
00098   SmallVector<CaseInfo, 16> Cases;
00099   SmallVector<DeclRefExpr *, 16> LocalRefs;
00100 
00101 public:
00102   ProtectedScopeFixer(BodyContext &BodyCtx)
00103     : Pass(BodyCtx.getMigrationContext().Pass),
00104       SM(Pass.Ctx.getSourceManager()) {
00105 
00106     CaseCollector(BodyCtx.getParentMap(), Cases)
00107         .TraverseStmt(BodyCtx.getTopStmt());
00108     LocalRefsCollector(LocalRefs).TraverseStmt(BodyCtx.getTopStmt());
00109 
00110     SourceRange BodyRange = BodyCtx.getTopStmt()->getSourceRange();
00111     const CapturedDiagList &DiagList = Pass.getDiags();
00112     // Copy the diagnostics so we don't have to worry about invaliding iterators
00113     // from the diagnostic list.
00114     SmallVector<StoredDiagnostic, 16> StoredDiags;
00115     StoredDiags.append(DiagList.begin(), DiagList.end());
00116     SmallVectorImpl<StoredDiagnostic>::iterator
00117         I = StoredDiags.begin(), E = StoredDiags.end();
00118     while (I != E) {
00119       if (I->getID() == diag::err_switch_into_protected_scope &&
00120           isInRange(I->getLocation(), BodyRange)) {
00121         handleProtectedScopeError(I, E);
00122         continue;
00123       }
00124       ++I;
00125     }
00126   }
00127 
00128   void handleProtectedScopeError(
00129                              SmallVectorImpl<StoredDiagnostic>::iterator &DiagI,
00130                              SmallVectorImpl<StoredDiagnostic>::iterator DiagE){
00131     Transaction Trans(Pass.TA);
00132     assert(DiagI->getID() == diag::err_switch_into_protected_scope);
00133     SourceLocation ErrLoc = DiagI->getLocation();
00134     bool handledAllNotes = true;
00135     ++DiagI;
00136     for (; DiagI != DiagE && DiagI->getLevel() == DiagnosticsEngine::Note;
00137          ++DiagI) {
00138       if (!handleProtectedNote(*DiagI))
00139         handledAllNotes = false;
00140     }
00141 
00142     if (handledAllNotes)
00143       Pass.TA.clearDiagnostic(diag::err_switch_into_protected_scope, ErrLoc);
00144   }
00145 
00146   bool handleProtectedNote(const StoredDiagnostic &Diag) {
00147     assert(Diag.getLevel() == DiagnosticsEngine::Note);
00148 
00149     for (unsigned i = 0; i != Cases.size(); i++) {
00150       CaseInfo &info = Cases[i];
00151       if (isInRange(Diag.getLocation(), info.Range)) {
00152 
00153         if (info.State == CaseInfo::St_Unchecked)
00154           tryFixing(info);
00155         assert(info.State != CaseInfo::St_Unchecked);
00156 
00157         if (info.State == CaseInfo::St_Fixed) {
00158           Pass.TA.clearDiagnostic(Diag.getID(), Diag.getLocation());
00159           return true;
00160         }
00161         return false;
00162       }
00163     }
00164 
00165     return false;
00166   }
00167 
00168   void tryFixing(CaseInfo &info) {
00169     assert(info.State == CaseInfo::St_Unchecked);
00170     if (hasVarReferencedOutside(info)) {
00171       info.State = CaseInfo::St_CannotFix;
00172       return;
00173     }
00174 
00175     Pass.TA.insertAfterToken(info.SC->getColonLoc(), " {");
00176     Pass.TA.insert(info.Range.getEnd(), "}\n");
00177     info.State = CaseInfo::St_Fixed;
00178   }
00179 
00180   bool hasVarReferencedOutside(CaseInfo &info) {
00181     for (unsigned i = 0, e = LocalRefs.size(); i != e; ++i) {
00182       DeclRefExpr *DRE = LocalRefs[i];
00183       if (isInRange(DRE->getDecl()->getLocation(), info.Range) &&
00184           !isInRange(DRE->getLocation(), info.Range))
00185         return true;
00186     }
00187     return false;
00188   }
00189 
00190   bool isInRange(SourceLocation Loc, SourceRange R) {
00191     if (Loc.isInvalid())
00192       return false;
00193     return !SM.isBeforeInTranslationUnit(Loc, R.getBegin()) &&
00194             SM.isBeforeInTranslationUnit(Loc, R.getEnd());
00195   }
00196 };
00197 
00198 } // anonymous namespace
00199 
00200 void ProtectedScopeTraverser::traverseBody(BodyContext &BodyCtx) {
00201   ProtectedScopeFixer Fix(BodyCtx);
00202 }