diff options
Diffstat (limited to 'clang/lib/Sema/SemaDecl.cpp')
-rw-r--r-- | clang/lib/Sema/SemaDecl.cpp | 268 |
1 files changed, 1 insertions, 267 deletions
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 2f57a49f9e2..887c86cef59 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -2905,272 +2905,6 @@ Sema::DeclPtrTy Sema::ActOnStartOfFunctionDef(Scope *FnBodyScope, DeclPtrTy D) { return DeclPtrTy::make(FD); } -/// JumpScopeChecker - This object is used by Sema to diagnose invalid jumps -/// into VLA and other protected scopes. For example, this rejects: -/// goto L; -/// int a[n]; -/// L: -/// -namespace { -class JumpScopeChecker { - Sema &S; - - /// GotoScope - This is a record that we use to keep track of all of the scopes - /// that are introduced by VLAs and other things that scope jumps like gotos. - /// This scope tree has nothing to do with the source scope tree, because you - /// can have multiple VLA scopes per compound statement, and most compound - /// statements don't introduce any scopes. - struct GotoScope { - /// ParentScope - The index in ScopeMap of the parent scope. This is 0 for - /// the parent scope is the function body. - unsigned ParentScope; - - /// Diag - The diagnostic to emit if there is a jump into this scope. - unsigned Diag; - - /// Loc - Location to emit the diagnostic. - SourceLocation Loc; - - GotoScope(unsigned parentScope, unsigned diag, SourceLocation L) - : ParentScope(parentScope), Diag(diag), Loc(L) {} - }; - - llvm::SmallVector<GotoScope, 48> Scopes; - llvm::DenseMap<Stmt*, unsigned> LabelAndGotoScopes; - llvm::SmallVector<Stmt*, 16> Jumps; -public: - JumpScopeChecker(CompoundStmt *Body, Sema &S); -private: - void BuildScopeInformation(Stmt *S, unsigned ParentScope); - void VerifyJumps(); - void CheckJump(Stmt *From, Stmt *To, - SourceLocation DiagLoc, unsigned JumpDiag); -}; -} // end anonymous namespace - - -JumpScopeChecker::JumpScopeChecker(CompoundStmt *Body, Sema &s) : S(s) { - // Add a scope entry for function scope. - Scopes.push_back(GotoScope(~0U, ~0U, SourceLocation())); - - // Build information for the top level compound statement, so that we have a - // defined scope record for every "goto" and label. - BuildScopeInformation(Body, 0); - - // Check that all jumps we saw are kosher. - VerifyJumps(); -} - -/// GetDiagForGotoScopeDecl - If this decl induces a new goto scope, return a -/// diagnostic that should be emitted if control goes over it. If not, return 0. -static unsigned GetDiagForGotoScopeDecl(const Decl *D) { - if (const VarDecl *VD = dyn_cast<VarDecl>(D)) { - if (VD->getType()->isVariablyModifiedType()) - return diag::note_protected_by_vla; - if (VD->hasAttr<CleanupAttr>()) - return diag::note_protected_by_cleanup; - } else if (const TypedefDecl *TD = dyn_cast<TypedefDecl>(D)) { - if (TD->getUnderlyingType()->isVariablyModifiedType()) - return diag::note_protected_by_vla_typedef; - } - - return 0; -} - - -/// BuildScopeInformation - The statements from CI to CE are known to form a -/// coherent VLA scope with a specified parent node. Walk through the -/// statements, adding any labels or gotos to LabelAndGotoScopes and recursively -/// walking the AST as needed. -void JumpScopeChecker::BuildScopeInformation(Stmt *S, unsigned ParentScope) { - - // If we found a label, remember that it is in ParentScope scope. - if (isa<LabelStmt>(S) || isa<DefaultStmt>(S) || isa<CaseStmt>(S)) { - LabelAndGotoScopes[S] = ParentScope; - } else if (isa<GotoStmt>(S) || isa<SwitchStmt>(S) || - isa<IndirectGotoStmt>(S) || isa<AddrLabelExpr>(S)) { - // Remember both what scope a goto is in as well as the fact that we have - // it. This makes the second scan not have to walk the AST again. - LabelAndGotoScopes[S] = ParentScope; - Jumps.push_back(S); - } - - for (Stmt::child_iterator CI = S->child_begin(), E = S->child_end(); CI != E; - ++CI) { - Stmt *SubStmt = *CI; - if (SubStmt == 0) continue; - - // FIXME: diagnose jumps past initialization: required in C++, warning in C. - // goto L; int X = 4; L: ; - // FIXME: what about jumps into C++ catch blocks, what are the rules? - - // If this is a declstmt with a VLA definition, it defines a scope from here - // to the end of the containing context. - if (DeclStmt *DS = dyn_cast<DeclStmt>(SubStmt)) { - // The decl statement creates a scope if any of the decls in it are VLAs or - // have the cleanup attribute. - for (DeclStmt::decl_iterator I = DS->decl_begin(), E = DS->decl_end(); - I != E; ++I) { - // If this decl causes a new scope, push and switch to it. - if (unsigned Diag = GetDiagForGotoScopeDecl(*I)) { - Scopes.push_back(GotoScope(ParentScope, Diag, (*I)->getLocation())); - ParentScope = Scopes.size()-1; - } - - // If the decl has an initializer, walk it with the potentially new - // scope we just installed. - if (VarDecl *VD = dyn_cast<VarDecl>(*I)) - if (Expr *Init = VD->getInit()) - BuildScopeInformation(Init, ParentScope); - } - continue; - } - - // Disallow jumps into any part of an @try statement by pushing a scope and - // walking all sub-stmts in that scope. - if (ObjCAtTryStmt *AT = dyn_cast<ObjCAtTryStmt>(SubStmt)) { - // Recursively walk the AST for the @try part. - Scopes.push_back(GotoScope(ParentScope,diag::note_protected_by_objc_try, - AT->getAtTryLoc())); - if (Stmt *TryPart = AT->getTryBody()) - BuildScopeInformation(TryPart, Scopes.size()-1); - - // Jump from the catch to the finally or try is not valid. - for (ObjCAtCatchStmt *AC = AT->getCatchStmts(); AC; - AC = AC->getNextCatchStmt()) { - Scopes.push_back(GotoScope(ParentScope, - diag::note_protected_by_objc_catch, - AC->getAtCatchLoc())); - // @catches are nested and it isn't - BuildScopeInformation(AC->getCatchBody(), Scopes.size()-1); - } - - // Jump from the finally to the try or catch is not valid. - if (ObjCAtFinallyStmt *AF = AT->getFinallyStmt()) { - Scopes.push_back(GotoScope(ParentScope, - diag::note_protected_by_objc_finally, - AF->getAtFinallyLoc())); - BuildScopeInformation(AF, Scopes.size()-1); - } - - continue; - } - - // Recursively walk the AST. - BuildScopeInformation(SubStmt, ParentScope); - } -} - -void JumpScopeChecker::VerifyJumps() { - while (!Jumps.empty()) { - Stmt *Jump = Jumps.pop_back_val(); - - if (GotoStmt *GS = dyn_cast<GotoStmt>(Jump)) { - CheckJump(GS, GS->getLabel(), GS->getGotoLoc(), - diag::err_goto_into_protected_scope); - continue; - } - - if (SwitchStmt *SS = dyn_cast<SwitchStmt>(Jump)) { - for (SwitchCase *SC = SS->getSwitchCaseList(); SC; - SC = SC->getNextSwitchCase()) { - assert(LabelAndGotoScopes.count(SC) && "Case not visited?"); - CheckJump(SS, SC, SC->getLocStart(), - diag::err_switch_into_protected_scope); - } - continue; - } - - - unsigned DiagnosticScope; - - // We don't know where an indirect goto goes, require that it be at the - // top level of scoping. - if (IndirectGotoStmt *IG = dyn_cast<IndirectGotoStmt>(Jump)) { - assert(LabelAndGotoScopes.count(Jump) && - "Jump didn't get added to scopes?"); - unsigned GotoScope = LabelAndGotoScopes[IG]; - if (GotoScope == 0) continue; // indirect jump is ok. - S.Diag(IG->getGotoLoc(), diag::err_indirect_goto_in_protected_scope); - DiagnosticScope = GotoScope; - } else { - // We model &&Label as a jump for purposes of scope tracking. We actually - // don't care *where* the address of label is, but we require the *label - // itself* to be in scope 0. If it is nested inside of a VLA scope, then - // it is possible for an indirect goto to illegally enter the VLA scope by - // indirectly jumping to the label. - assert(isa<AddrLabelExpr>(Jump) && "Unknown jump type"); - LabelStmt *TheLabel = cast<AddrLabelExpr>(Jump)->getLabel(); - - assert(LabelAndGotoScopes.count(TheLabel) && - "Referenced label didn't get added to scopes?"); - unsigned LabelScope = LabelAndGotoScopes[TheLabel]; - if (LabelScope == 0) continue; // Addr of label is ok. - - S.Diag(Jump->getLocStart(), diag::err_addr_of_label_in_protected_scope); - DiagnosticScope = LabelScope; - } - - // Report all the things that would be skipped over by this &&label or - // indirect goto. - while (DiagnosticScope != 0) { - S.Diag(Scopes[DiagnosticScope].Loc, Scopes[DiagnosticScope].Diag); - DiagnosticScope = Scopes[DiagnosticScope].ParentScope; - } - } -} - -/// CheckJump - Validate that the specified jump statement is valid: that it is -/// jumping within or out of its current scope, not into a deeper one. -void JumpScopeChecker::CheckJump(Stmt *From, Stmt *To, - SourceLocation DiagLoc, unsigned JumpDiag) { - assert(LabelAndGotoScopes.count(From) && "Jump didn't get added to scopes?"); - unsigned FromScope = LabelAndGotoScopes[From]; - - assert(LabelAndGotoScopes.count(To) && "Jump didn't get added to scopes?"); - unsigned ToScope = LabelAndGotoScopes[To]; - - // Common case: exactly the same scope, which is fine. - if (FromScope == ToScope) return; - - // The only valid mismatch jump case happens when the jump is more deeply - // nested inside the jump target. Do a quick scan to see if the jump is valid - // because valid code is more common than invalid code. - unsigned TestScope = Scopes[FromScope].ParentScope; - while (TestScope != ~0U) { - // If we found the jump target, then we're jumping out of our current scope, - // which is perfectly fine. - if (TestScope == ToScope) return; - - // Otherwise, scan up the hierarchy. - TestScope = Scopes[TestScope].ParentScope; - } - - // If we get here, then we know we have invalid code. Diagnose the bad jump, - // and then emit a note at each VLA being jumped out of. - S.Diag(DiagLoc, JumpDiag); - - // Eliminate the common prefix of the jump and the target. Start by - // linearizing both scopes, reversing them as we go. - std::vector<unsigned> FromScopes, ToScopes; - for (TestScope = FromScope; TestScope != ~0U; - TestScope = Scopes[TestScope].ParentScope) - FromScopes.push_back(TestScope); - for (TestScope = ToScope; TestScope != ~0U; - TestScope = Scopes[TestScope].ParentScope) - ToScopes.push_back(TestScope); - - // Remove any common entries (such as the top-level function scope). - while (!FromScopes.empty() && FromScopes.back() == ToScopes.back()) { - FromScopes.pop_back(); - ToScopes.pop_back(); - } - - // Emit diagnostics for whatever is left in ToScopes. - for (unsigned i = 0, e = ToScopes.size(); i != e; ++i) - S.Diag(Scopes[ToScopes[i]].Loc, Scopes[ToScopes[i]].Diag); -} - Sema::DeclPtrTy Sema::ActOnFinishFunctionBody(DeclPtrTy D, StmtArg BodyArg) { Decl *dcl = D.getAs<Decl>(); @@ -3230,7 +2964,7 @@ Sema::DeclPtrTy Sema::ActOnFinishFunctionBody(DeclPtrTy D, StmtArg BodyArg) { if (!Body) return D; // Verify that that gotos and switch cases don't jump into scopes illegally. - JumpScopeChecker(Body, *this); + DiagnoseInvalidJumps(Body); return D; } |