From: Chris Lattner Date: Sun, 19 Apr 2009 04:46:21 +0000 (+0000) Subject: move jump scope checking and related code out into its own file, SemaDecl.cpp is X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=5af280ce21af061f96b5b5b752746871e364ba99;p=clang move jump scope checking and related code out into its own file, SemaDecl.cpp is already too large. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@69505 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/clang.xcodeproj/project.pbxproj b/clang.xcodeproj/project.pbxproj index 170b163dff..1c298911a0 100644 --- a/clang.xcodeproj/project.pbxproj +++ b/clang.xcodeproj/project.pbxproj @@ -174,6 +174,7 @@ DEC8D9910A9433CD00353FCA /* Decl.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = DEC8D9900A9433CD00353FCA /* Decl.h */; }; DEC8D9A40A94346E00353FCA /* AST.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = DEC8D9A30A94346E00353FCA /* AST.h */; }; DECAB0D00DB3C84200E13CCB /* RewriteRope.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DECAB0CF0DB3C84200E13CCB /* RewriteRope.cpp */; }; + DECB6D650F9AE26600F5FBC7 /* JumpDiagnostics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DECB6D640F9AE26600F5FBC7 /* JumpDiagnostics.cpp */; }; DED626C90AE0C065001E80A4 /* TargetInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DED626C80AE0C065001E80A4 /* TargetInfo.cpp */; }; DED62ABB0AE2EDF1001E80A4 /* Decl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DED62ABA0AE2EDF1001E80A4 /* Decl.cpp */; }; DED676D10B6C786700AAD4A3 /* Builtins.def in CopyFiles */ = {isa = PBXBuildFile; fileRef = DED676D00B6C786700AAD4A3 /* Builtins.def */; }; @@ -580,6 +581,7 @@ DEC8D9900A9433CD00353FCA /* Decl.h */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 2; lastKnownFileType = sourcecode.c.h; name = Decl.h; path = clang/AST/Decl.h; sourceTree = ""; tabWidth = 2; }; DEC8D9A30A94346E00353FCA /* AST.h */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 2; lastKnownFileType = sourcecode.c.h; name = AST.h; path = clang/AST/AST.h; sourceTree = ""; tabWidth = 2; }; DECAB0CF0DB3C84200E13CCB /* RewriteRope.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RewriteRope.cpp; path = lib/Rewrite/RewriteRope.cpp; sourceTree = ""; }; + DECB6D640F9AE26600F5FBC7 /* JumpDiagnostics.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = JumpDiagnostics.cpp; path = lib/Sema/JumpDiagnostics.cpp; sourceTree = ""; }; DED626C80AE0C065001E80A4 /* TargetInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 2; lastKnownFileType = sourcecode.cpp.cpp; path = TargetInfo.cpp; sourceTree = ""; tabWidth = 2; }; DED62ABA0AE2EDF1001E80A4 /* Decl.cpp */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 2; lastKnownFileType = sourcecode.cpp.cpp; name = Decl.cpp; path = lib/AST/Decl.cpp; sourceTree = ""; tabWidth = 2; usesTabs = 0; }; DED676D00B6C786700AAD4A3 /* Builtins.def */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 2; lastKnownFileType = text; name = Builtins.def; path = clang/AST/Builtins.def; sourceTree = ""; tabWidth = 2; }; @@ -999,6 +1001,7 @@ 35585DBD0EAFBC4500D0A97A /* CXXFieldCollector.h */, 3527124F0DAFE54700C76352 /* IdentifierResolver.h */, 352712500DAFE54700C76352 /* IdentifierResolver.cpp */, + DECB6D640F9AE26600F5FBC7 /* JumpDiagnostics.cpp */, DE67E7190C020F4F00F66BC5 /* ParseAST.cpp */, DE67E7140C020EDF00F66BC5 /* Sema.h */, DE67E7160C020EE400F66BC5 /* Sema.cpp */, @@ -1653,6 +1656,7 @@ DEF165750F8FB3510098507F /* PCHReader.cpp in Sources */, DEF165900F8FB3FC0098507F /* GeneratePCH.cpp in Sources */, DEF168400F9548DC0098507F /* FixItRewriter.cpp in Sources */, + DECB6D650F9AE26600F5FBC7 /* JumpDiagnostics.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/lib/Sema/CMakeLists.txt b/lib/Sema/CMakeLists.txt index b349636d8f..20ccd57f9c 100644 --- a/lib/Sema/CMakeLists.txt +++ b/lib/Sema/CMakeLists.txt @@ -2,6 +2,7 @@ set(LLVM_NO_RTTI 1) add_clang_library(clangSema IdentifierResolver.cpp + JumpDiagnostics.cpp ParseAST.cpp Sema.cpp SemaAccess.cpp diff --git a/lib/Sema/JumpDiagnostics.cpp b/lib/Sema/JumpDiagnostics.cpp new file mode 100644 index 0000000000..82db0bbd2c --- /dev/null +++ b/lib/Sema/JumpDiagnostics.cpp @@ -0,0 +1,290 @@ +//===--- JumpDiagnostics.cpp - Analyze Jump Targets for VLA issues --------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the JumpScopeChecker class, which is used to diagnose +// jumps that enter a VLA scope in an invalid way. +// +//===----------------------------------------------------------------------===// + +#include "Sema.h" +#include "clang/AST/Expr.h" +using namespace clang; + +namespace { + +/// 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: +/// +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 Scopes; + llvm::DenseMap LabelAndGotoScopes; + llvm::SmallVector Jumps; +public: + JumpScopeChecker(Stmt *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(Stmt *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(D)) { + if (VD->getType()->isVariablyModifiedType()) + return diag::note_protected_by_vla; + if (VD->hasAttr()) + return diag::note_protected_by_cleanup; + } else if (const TypedefDecl *TD = dyn_cast(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(S) || isa(S) || isa(S)) { + LabelAndGotoScopes[S] = ParentScope; + } else if (isa(S) || isa(S) || + isa(S) || isa(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(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(*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(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); + } +} + +/// VerifyJumps - Verify each element of the Jumps array to see if they are +/// valid, emitting diagnostics if not. +void JumpScopeChecker::VerifyJumps() { + while (!Jumps.empty()) { + Stmt *Jump = Jumps.pop_back_val(); + + // With a goto, + if (GotoStmt *GS = dyn_cast(Jump)) { + CheckJump(GS, GS->getLabel(), GS->getGotoLoc(), + diag::err_goto_into_protected_scope); + continue; + } + + if (SwitchStmt *SS = dyn_cast(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(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(Jump) && "Unknown jump type"); + LabelStmt *TheLabel = cast(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 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); +} + +void Sema::DiagnoseInvalidJumps(Stmt *Body) { + JumpScopeChecker(Body, *this); +} diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 7818ae171b..b08a120e58 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -440,6 +440,7 @@ public: virtual void ActOnStartOfObjCMethodDef(Scope *S, DeclPtrTy D); virtual DeclPtrTy ActOnFinishFunctionBody(DeclPtrTy Decl, StmtArg Body); + void DiagnoseInvalidJumps(Stmt *Body); virtual DeclPtrTy ActOnFileScopeAsmDecl(SourceLocation Loc, ExprArg expr); /// Scope actions. diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 2f57a49f9e..887c86cef5 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/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 Scopes; - llvm::DenseMap LabelAndGotoScopes; - llvm::SmallVector 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(D)) { - if (VD->getType()->isVariablyModifiedType()) - return diag::note_protected_by_vla; - if (VD->hasAttr()) - return diag::note_protected_by_cleanup; - } else if (const TypedefDecl *TD = dyn_cast(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(S) || isa(S) || isa(S)) { - LabelAndGotoScopes[S] = ParentScope; - } else if (isa(S) || isa(S) || - isa(S) || isa(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(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(*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(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(Jump)) { - CheckJump(GS, GS->getLabel(), GS->getGotoLoc(), - diag::err_goto_into_protected_scope); - continue; - } - - if (SwitchStmt *SS = dyn_cast(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(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(Jump) && "Unknown jump type"); - LabelStmt *TheLabel = cast(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 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(); @@ -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; }