JumpDiagnostics.cpp revision 207619
1193326Sed//===--- JumpDiagnostics.cpp - Analyze Jump Targets for VLA issues --------===// 2193326Sed// 3193326Sed// The LLVM Compiler Infrastructure 4193326Sed// 5193326Sed// This file is distributed under the University of Illinois Open Source 6193326Sed// License. See LICENSE.TXT for details. 7193326Sed// 8193326Sed//===----------------------------------------------------------------------===// 9193326Sed// 10193326Sed// This file implements the JumpScopeChecker class, which is used to diagnose 11193326Sed// jumps that enter a VLA scope in an invalid way. 12193326Sed// 13193326Sed//===----------------------------------------------------------------------===// 14193326Sed 15193326Sed#include "Sema.h" 16193326Sed#include "clang/AST/Expr.h" 17193326Sed#include "clang/AST/StmtObjC.h" 18193326Sed#include "clang/AST/StmtCXX.h" 19193326Sedusing namespace clang; 20193326Sed 21193326Sednamespace { 22193326Sed 23193326Sed/// JumpScopeChecker - This object is used by Sema to diagnose invalid jumps 24193326Sed/// into VLA and other protected scopes. For example, this rejects: 25193326Sed/// goto L; 26193326Sed/// int a[n]; 27193326Sed/// L: 28193326Sed/// 29193326Sedclass JumpScopeChecker { 30193326Sed Sema &S; 31198092Srdivacky 32193326Sed /// GotoScope - This is a record that we use to keep track of all of the 33193326Sed /// scopes that are introduced by VLAs and other things that scope jumps like 34193326Sed /// gotos. This scope tree has nothing to do with the source scope tree, 35193326Sed /// because you can have multiple VLA scopes per compound statement, and most 36193326Sed /// compound statements don't introduce any scopes. 37193326Sed struct GotoScope { 38193326Sed /// ParentScope - The index in ScopeMap of the parent scope. This is 0 for 39193326Sed /// the parent scope is the function body. 40193326Sed unsigned ParentScope; 41198092Srdivacky 42193326Sed /// Diag - The diagnostic to emit if there is a jump into this scope. 43193326Sed unsigned Diag; 44198092Srdivacky 45193326Sed /// Loc - Location to emit the diagnostic. 46193326Sed SourceLocation Loc; 47198092Srdivacky 48193326Sed GotoScope(unsigned parentScope, unsigned diag, SourceLocation L) 49193326Sed : ParentScope(parentScope), Diag(diag), Loc(L) {} 50193326Sed }; 51198092Srdivacky 52193326Sed llvm::SmallVector<GotoScope, 48> Scopes; 53193326Sed llvm::DenseMap<Stmt*, unsigned> LabelAndGotoScopes; 54193326Sed llvm::SmallVector<Stmt*, 16> Jumps; 55193326Sedpublic: 56193326Sed JumpScopeChecker(Stmt *Body, Sema &S); 57193326Sedprivate: 58193326Sed void BuildScopeInformation(Stmt *S, unsigned ParentScope); 59193326Sed void VerifyJumps(); 60193326Sed void CheckJump(Stmt *From, Stmt *To, 61193326Sed SourceLocation DiagLoc, unsigned JumpDiag); 62193326Sed}; 63193326Sed} // end anonymous namespace 64193326Sed 65193326Sed 66193326SedJumpScopeChecker::JumpScopeChecker(Stmt *Body, Sema &s) : S(s) { 67193326Sed // Add a scope entry for function scope. 68193326Sed Scopes.push_back(GotoScope(~0U, ~0U, SourceLocation())); 69198092Srdivacky 70193326Sed // Build information for the top level compound statement, so that we have a 71193326Sed // defined scope record for every "goto" and label. 72193326Sed BuildScopeInformation(Body, 0); 73198092Srdivacky 74193326Sed // Check that all jumps we saw are kosher. 75193326Sed VerifyJumps(); 76193326Sed} 77198092Srdivacky 78193326Sed/// GetDiagForGotoScopeDecl - If this decl induces a new goto scope, return a 79193326Sed/// diagnostic that should be emitted if control goes over it. If not, return 0. 80204643Srdivackystatic unsigned GetDiagForGotoScopeDecl(const Decl *D, bool isCPlusPlus) { 81193326Sed if (const VarDecl *VD = dyn_cast<VarDecl>(D)) { 82193326Sed if (VD->getType()->isVariablyModifiedType()) 83193326Sed return diag::note_protected_by_vla; 84195341Sed if (VD->hasAttr<CleanupAttr>()) 85193326Sed return diag::note_protected_by_cleanup; 86198092Srdivacky if (VD->hasAttr<BlocksAttr>()) 87198092Srdivacky return diag::note_protected_by___block; 88204962Srdivacky // FIXME: In C++0x, we have to check more conditions than "did we 89204962Srdivacky // just give it an initializer?". See 6.7p3. 90204643Srdivacky if (isCPlusPlus && VD->hasLocalStorage() && VD->hasInit()) 91204643Srdivacky return diag::note_protected_by_variable_init; 92204643Srdivacky 93193326Sed } else if (const TypedefDecl *TD = dyn_cast<TypedefDecl>(D)) { 94193326Sed if (TD->getUnderlyingType()->isVariablyModifiedType()) 95193326Sed return diag::note_protected_by_vla_typedef; 96193326Sed } 97198092Srdivacky 98193326Sed return 0; 99193326Sed} 100193326Sed 101193326Sed 102193326Sed/// BuildScopeInformation - The statements from CI to CE are known to form a 103193326Sed/// coherent VLA scope with a specified parent node. Walk through the 104193326Sed/// statements, adding any labels or gotos to LabelAndGotoScopes and recursively 105193326Sed/// walking the AST as needed. 106193326Sedvoid JumpScopeChecker::BuildScopeInformation(Stmt *S, unsigned ParentScope) { 107198092Srdivacky 108193326Sed // If we found a label, remember that it is in ParentScope scope. 109193326Sed if (isa<LabelStmt>(S) || isa<DefaultStmt>(S) || isa<CaseStmt>(S)) { 110193326Sed LabelAndGotoScopes[S] = ParentScope; 111193326Sed } else if (isa<GotoStmt>(S) || isa<SwitchStmt>(S) || 112193326Sed isa<IndirectGotoStmt>(S) || isa<AddrLabelExpr>(S)) { 113193326Sed // Remember both what scope a goto is in as well as the fact that we have 114193326Sed // it. This makes the second scan not have to walk the AST again. 115193326Sed LabelAndGotoScopes[S] = ParentScope; 116193326Sed Jumps.push_back(S); 117193326Sed } 118198092Srdivacky 119193326Sed for (Stmt::child_iterator CI = S->child_begin(), E = S->child_end(); CI != E; 120193326Sed ++CI) { 121193326Sed Stmt *SubStmt = *CI; 122193326Sed if (SubStmt == 0) continue; 123198092Srdivacky 124204643Srdivacky bool isCPlusPlus = this->S.getLangOptions().CPlusPlus; 125193326Sed 126193326Sed // If this is a declstmt with a VLA definition, it defines a scope from here 127193326Sed // to the end of the containing context. 128193326Sed if (DeclStmt *DS = dyn_cast<DeclStmt>(SubStmt)) { 129204643Srdivacky // The decl statement creates a scope if any of the decls in it are VLAs 130204643Srdivacky // or have the cleanup attribute. 131193326Sed for (DeclStmt::decl_iterator I = DS->decl_begin(), E = DS->decl_end(); 132193326Sed I != E; ++I) { 133193326Sed // If this decl causes a new scope, push and switch to it. 134204643Srdivacky if (unsigned Diag = GetDiagForGotoScopeDecl(*I, isCPlusPlus)) { 135193326Sed Scopes.push_back(GotoScope(ParentScope, Diag, (*I)->getLocation())); 136193326Sed ParentScope = Scopes.size()-1; 137193326Sed } 138198092Srdivacky 139193326Sed // If the decl has an initializer, walk it with the potentially new 140193326Sed // scope we just installed. 141193326Sed if (VarDecl *VD = dyn_cast<VarDecl>(*I)) 142193326Sed if (Expr *Init = VD->getInit()) 143193326Sed BuildScopeInformation(Init, ParentScope); 144193326Sed } 145193326Sed continue; 146193326Sed } 147193326Sed 148193326Sed // Disallow jumps into any part of an @try statement by pushing a scope and 149193326Sed // walking all sub-stmts in that scope. 150193326Sed if (ObjCAtTryStmt *AT = dyn_cast<ObjCAtTryStmt>(SubStmt)) { 151193326Sed // Recursively walk the AST for the @try part. 152193326Sed Scopes.push_back(GotoScope(ParentScope,diag::note_protected_by_objc_try, 153193326Sed AT->getAtTryLoc())); 154193326Sed if (Stmt *TryPart = AT->getTryBody()) 155193326Sed BuildScopeInformation(TryPart, Scopes.size()-1); 156193326Sed 157193326Sed // Jump from the catch to the finally or try is not valid. 158207619Srdivacky for (unsigned I = 0, N = AT->getNumCatchStmts(); I != N; ++I) { 159207619Srdivacky ObjCAtCatchStmt *AC = AT->getCatchStmt(I); 160193326Sed Scopes.push_back(GotoScope(ParentScope, 161193326Sed diag::note_protected_by_objc_catch, 162193326Sed AC->getAtCatchLoc())); 163198092Srdivacky // @catches are nested and it isn't 164193326Sed BuildScopeInformation(AC->getCatchBody(), Scopes.size()-1); 165193326Sed } 166198092Srdivacky 167193326Sed // Jump from the finally to the try or catch is not valid. 168193326Sed if (ObjCAtFinallyStmt *AF = AT->getFinallyStmt()) { 169193326Sed Scopes.push_back(GotoScope(ParentScope, 170193326Sed diag::note_protected_by_objc_finally, 171193326Sed AF->getAtFinallyLoc())); 172193326Sed BuildScopeInformation(AF, Scopes.size()-1); 173193326Sed } 174198092Srdivacky 175193326Sed continue; 176193326Sed } 177198092Srdivacky 178193326Sed // Disallow jumps into the protected statement of an @synchronized, but 179193326Sed // allow jumps into the object expression it protects. 180193326Sed if (ObjCAtSynchronizedStmt *AS = dyn_cast<ObjCAtSynchronizedStmt>(SubStmt)){ 181193326Sed // Recursively walk the AST for the @synchronized object expr, it is 182193326Sed // evaluated in the normal scope. 183193326Sed BuildScopeInformation(AS->getSynchExpr(), ParentScope); 184198092Srdivacky 185193326Sed // Recursively walk the AST for the @synchronized part, protected by a new 186193326Sed // scope. 187193326Sed Scopes.push_back(GotoScope(ParentScope, 188193326Sed diag::note_protected_by_objc_synchronized, 189193326Sed AS->getAtSynchronizedLoc())); 190193326Sed BuildScopeInformation(AS->getSynchBody(), Scopes.size()-1); 191193326Sed continue; 192193326Sed } 193193326Sed 194193326Sed // Disallow jumps into any part of a C++ try statement. This is pretty 195193326Sed // much the same as for Obj-C. 196193326Sed if (CXXTryStmt *TS = dyn_cast<CXXTryStmt>(SubStmt)) { 197193326Sed Scopes.push_back(GotoScope(ParentScope, diag::note_protected_by_cxx_try, 198193326Sed TS->getSourceRange().getBegin())); 199193326Sed if (Stmt *TryBlock = TS->getTryBlock()) 200193326Sed BuildScopeInformation(TryBlock, Scopes.size()-1); 201193326Sed 202193326Sed // Jump from the catch into the try is not allowed either. 203198092Srdivacky for (unsigned I = 0, E = TS->getNumHandlers(); I != E; ++I) { 204193326Sed CXXCatchStmt *CS = TS->getHandler(I); 205193326Sed Scopes.push_back(GotoScope(ParentScope, 206193326Sed diag::note_protected_by_cxx_catch, 207193326Sed CS->getSourceRange().getBegin())); 208193326Sed BuildScopeInformation(CS->getHandlerBlock(), Scopes.size()-1); 209193326Sed } 210193326Sed 211193326Sed continue; 212193326Sed } 213193326Sed 214193326Sed // Recursively walk the AST. 215193326Sed BuildScopeInformation(SubStmt, ParentScope); 216193326Sed } 217193326Sed} 218193326Sed 219193326Sed/// VerifyJumps - Verify each element of the Jumps array to see if they are 220193326Sed/// valid, emitting diagnostics if not. 221193326Sedvoid JumpScopeChecker::VerifyJumps() { 222193326Sed while (!Jumps.empty()) { 223193326Sed Stmt *Jump = Jumps.pop_back_val(); 224198092Srdivacky 225198092Srdivacky // With a goto, 226193326Sed if (GotoStmt *GS = dyn_cast<GotoStmt>(Jump)) { 227193326Sed CheckJump(GS, GS->getLabel(), GS->getGotoLoc(), 228193326Sed diag::err_goto_into_protected_scope); 229193326Sed continue; 230193326Sed } 231198092Srdivacky 232193326Sed if (SwitchStmt *SS = dyn_cast<SwitchStmt>(Jump)) { 233193326Sed for (SwitchCase *SC = SS->getSwitchCaseList(); SC; 234193326Sed SC = SC->getNextSwitchCase()) { 235193326Sed assert(LabelAndGotoScopes.count(SC) && "Case not visited?"); 236193326Sed CheckJump(SS, SC, SC->getLocStart(), 237193326Sed diag::err_switch_into_protected_scope); 238193326Sed } 239193326Sed continue; 240193326Sed } 241193326Sed 242193326Sed unsigned DiagnosticScope; 243198092Srdivacky 244193326Sed // We don't know where an indirect goto goes, require that it be at the 245193326Sed // top level of scoping. 246193326Sed if (IndirectGotoStmt *IG = dyn_cast<IndirectGotoStmt>(Jump)) { 247193326Sed assert(LabelAndGotoScopes.count(Jump) && 248193326Sed "Jump didn't get added to scopes?"); 249193326Sed unsigned GotoScope = LabelAndGotoScopes[IG]; 250193326Sed if (GotoScope == 0) continue; // indirect jump is ok. 251193326Sed S.Diag(IG->getGotoLoc(), diag::err_indirect_goto_in_protected_scope); 252193326Sed DiagnosticScope = GotoScope; 253193326Sed } else { 254193326Sed // We model &&Label as a jump for purposes of scope tracking. We actually 255193326Sed // don't care *where* the address of label is, but we require the *label 256193326Sed // itself* to be in scope 0. If it is nested inside of a VLA scope, then 257193326Sed // it is possible for an indirect goto to illegally enter the VLA scope by 258193326Sed // indirectly jumping to the label. 259193326Sed assert(isa<AddrLabelExpr>(Jump) && "Unknown jump type"); 260193326Sed LabelStmt *TheLabel = cast<AddrLabelExpr>(Jump)->getLabel(); 261198092Srdivacky 262193326Sed assert(LabelAndGotoScopes.count(TheLabel) && 263193326Sed "Referenced label didn't get added to scopes?"); 264193326Sed unsigned LabelScope = LabelAndGotoScopes[TheLabel]; 265193326Sed if (LabelScope == 0) continue; // Addr of label is ok. 266198092Srdivacky 267193326Sed S.Diag(Jump->getLocStart(), diag::err_addr_of_label_in_protected_scope); 268193326Sed DiagnosticScope = LabelScope; 269193326Sed } 270193326Sed 271193326Sed // Report all the things that would be skipped over by this &&label or 272193326Sed // indirect goto. 273193326Sed while (DiagnosticScope != 0) { 274193326Sed S.Diag(Scopes[DiagnosticScope].Loc, Scopes[DiagnosticScope].Diag); 275193326Sed DiagnosticScope = Scopes[DiagnosticScope].ParentScope; 276193326Sed } 277193326Sed } 278193326Sed} 279193326Sed 280193326Sed/// CheckJump - Validate that the specified jump statement is valid: that it is 281193326Sed/// jumping within or out of its current scope, not into a deeper one. 282193326Sedvoid JumpScopeChecker::CheckJump(Stmt *From, Stmt *To, 283193326Sed SourceLocation DiagLoc, unsigned JumpDiag) { 284193326Sed assert(LabelAndGotoScopes.count(From) && "Jump didn't get added to scopes?"); 285193326Sed unsigned FromScope = LabelAndGotoScopes[From]; 286193326Sed 287193326Sed assert(LabelAndGotoScopes.count(To) && "Jump didn't get added to scopes?"); 288193326Sed unsigned ToScope = LabelAndGotoScopes[To]; 289198092Srdivacky 290193326Sed // Common case: exactly the same scope, which is fine. 291193326Sed if (FromScope == ToScope) return; 292198092Srdivacky 293193326Sed // The only valid mismatch jump case happens when the jump is more deeply 294193326Sed // nested inside the jump target. Do a quick scan to see if the jump is valid 295193326Sed // because valid code is more common than invalid code. 296193326Sed unsigned TestScope = Scopes[FromScope].ParentScope; 297193326Sed while (TestScope != ~0U) { 298193326Sed // If we found the jump target, then we're jumping out of our current scope, 299193326Sed // which is perfectly fine. 300193326Sed if (TestScope == ToScope) return; 301198092Srdivacky 302193326Sed // Otherwise, scan up the hierarchy. 303193326Sed TestScope = Scopes[TestScope].ParentScope; 304193326Sed } 305198092Srdivacky 306193326Sed // If we get here, then we know we have invalid code. Diagnose the bad jump, 307193326Sed // and then emit a note at each VLA being jumped out of. 308193326Sed S.Diag(DiagLoc, JumpDiag); 309193326Sed 310193326Sed // Eliminate the common prefix of the jump and the target. Start by 311193326Sed // linearizing both scopes, reversing them as we go. 312193326Sed std::vector<unsigned> FromScopes, ToScopes; 313193326Sed for (TestScope = FromScope; TestScope != ~0U; 314193326Sed TestScope = Scopes[TestScope].ParentScope) 315193326Sed FromScopes.push_back(TestScope); 316193326Sed for (TestScope = ToScope; TestScope != ~0U; 317193326Sed TestScope = Scopes[TestScope].ParentScope) 318193326Sed ToScopes.push_back(TestScope); 319193326Sed 320193326Sed // Remove any common entries (such as the top-level function scope). 321193326Sed while (!FromScopes.empty() && FromScopes.back() == ToScopes.back()) { 322193326Sed FromScopes.pop_back(); 323193326Sed ToScopes.pop_back(); 324193326Sed } 325198092Srdivacky 326193326Sed // Emit diagnostics for whatever is left in ToScopes. 327193326Sed for (unsigned i = 0, e = ToScopes.size(); i != e; ++i) 328193326Sed S.Diag(Scopes[ToScopes[i]].Loc, Scopes[ToScopes[i]].Diag); 329193326Sed} 330193326Sed 331193326Sedvoid Sema::DiagnoseInvalidJumps(Stmt *Body) { 332199482Srdivacky (void)JumpScopeChecker(Body, *this); 333193326Sed} 334