zcov: / lib/Sema/JumpDiagnostics.cpp


Files: 1 Branches Taken: 92.5% 98 / 106
Generated: 2010-02-10 01:31 Branches Executed: 100.0% 106 / 106
Line Coverage: 100.0% 118 / 118


Programs: 2 Runs 3018


       1                 : //===--- JumpDiagnostics.cpp - Analyze Jump Targets for VLA issues --------===//
       2                 : //
       3                 : //                     The LLVM Compiler Infrastructure
       4                 : //
       5                 : // This file is distributed under the University of Illinois Open Source
       6                 : // License. See LICENSE.TXT for details.
       7                 : //
       8                 : //===----------------------------------------------------------------------===//
       9                 : //
      10                 : // This file implements the JumpScopeChecker class, which is used to diagnose
      11                 : // jumps that enter a VLA scope in an invalid way.
      12                 : //
      13                 : //===----------------------------------------------------------------------===//
      14                 : 
      15                 : #include "Sema.h"
      16                 : #include "clang/AST/Expr.h"
      17                 : #include "clang/AST/StmtObjC.h"
      18                 : #include "clang/AST/StmtCXX.h"
      19                 : using namespace clang;
      20                 : 
      21                 : namespace {
      22                 : 
      23                 : /// JumpScopeChecker - This object is used by Sema to diagnose invalid jumps
      24                 : /// into VLA and other protected scopes.  For example, this rejects:
      25                 : ///    goto L;
      26                 : ///    int a[n];
      27                 : ///  L:
      28                 : ///
      29              232: class JumpScopeChecker {
      30                 :   Sema &S;
      31                 : 
      32                 :   /// GotoScope - This is a record that we use to keep track of all of the
      33                 :   /// scopes that are introduced by VLAs and other things that scope jumps like
      34                 :   /// gotos.  This scope tree has nothing to do with the source scope tree,
      35                 :   /// because you can have multiple VLA scopes per compound statement, and most
      36                 :   /// compound statements don't introduce any scopes.
      37              643:   struct GotoScope {
      38                 :     /// ParentScope - The index in ScopeMap of the parent scope.  This is 0 for
      39                 :     /// the parent scope is the function body.
      40                 :     unsigned ParentScope;
      41                 : 
      42                 :     /// Diag - The diagnostic to emit if there is a jump into this scope.
      43                 :     unsigned Diag;
      44                 : 
      45                 :     /// Loc - Location to emit the diagnostic.
      46                 :     SourceLocation Loc;
      47                 : 
      48              643:     GotoScope(unsigned parentScope, unsigned diag, SourceLocation L)
      49              643:     : ParentScope(parentScope), Diag(diag), Loc(L) {}
      50                 :   };
      51                 : 
      52                 :   llvm::SmallVector<GotoScope, 48> Scopes;
      53                 :   llvm::DenseMap<Stmt*, unsigned> LabelAndGotoScopes;
      54                 :   llvm::SmallVector<Stmt*, 16> Jumps;
      55                 : public:
      56                 :   JumpScopeChecker(Stmt *Body, Sema &S);
      57                 : private:
      58                 :   void BuildScopeInformation(Stmt *S, unsigned ParentScope);
      59                 :   void VerifyJumps();
      60                 :   void CheckJump(Stmt *From, Stmt *To,
      61                 :                  SourceLocation DiagLoc, unsigned JumpDiag);
      62                 : };
      63                 : } // end anonymous namespace
      64                 : 
      65                 : 
      66              232: JumpScopeChecker::JumpScopeChecker(Stmt *Body, Sema &s) : S(s) {
      67                 :   // Add a scope entry for function scope.
      68              232:   Scopes.push_back(GotoScope(~0U, ~0U, SourceLocation()));
      69                 : 
      70                 :   // Build information for the top level compound statement, so that we have a
      71                 :   // defined scope record for every "goto" and label.
      72              232:   BuildScopeInformation(Body, 0);
      73                 : 
      74                 :   // Check that all jumps we saw are kosher.
      75              232:   VerifyJumps();
      76              232: }
      77                 : 
      78                 : /// GetDiagForGotoScopeDecl - If this decl induces a new goto scope, return a
      79                 : /// diagnostic that should be emitted if control goes over it. If not, return 0.
      80              391: static unsigned GetDiagForGotoScopeDecl(const Decl *D) {
                      369: branch 1 taken
                       22: branch 2 taken
      81              391:   if (const VarDecl *VD = dyn_cast<VarDecl>(D)) {
                       93: branch 3 taken
                      276: branch 4 taken
      82              369:     if (VD->getType()->isVariablyModifiedType())
      83               93:       return diag::note_protected_by_vla;
                        8: branch 1 taken
                      268: branch 2 taken
      84              276:     if (VD->hasAttr<CleanupAttr>())
      85                8:       return diag::note_protected_by_cleanup;
                      108: branch 1 taken
                      160: branch 2 taken
      86              268:     if (VD->hasAttr<BlocksAttr>())
      87              108:       return diag::note_protected_by___block;
                       17: branch 1 taken
                        5: branch 2 taken
      88               22:   } else if (const TypedefDecl *TD = dyn_cast<TypedefDecl>(D)) {
                       14: branch 3 taken
                        3: branch 4 taken
      89               17:     if (TD->getUnderlyingType()->isVariablyModifiedType())
      90               14:       return diag::note_protected_by_vla_typedef;
      91                 :   }
      92                 : 
      93              168:   return 0;
      94                 : }
      95                 : 
      96                 : 
      97                 : /// BuildScopeInformation - The statements from CI to CE are known to form a
      98                 : /// coherent VLA scope with a specified parent node.  Walk through the
      99                 : /// statements, adding any labels or gotos to LabelAndGotoScopes and recursively
     100                 : /// walking the AST as needed.
     101             2588: void JumpScopeChecker::BuildScopeInformation(Stmt *S, unsigned ParentScope) {
     102                 : 
     103                 :   // If we found a label, remember that it is in ParentScope scope.
                     2538: branch 1 taken
                       50: branch 2 taken
                     2535: branch 4 taken
                        3: branch 5 taken
                        9: branch 7 taken
                     2526: branch 8 taken
                       62: branch 9 taken
                     2526: branch 10 taken
     104             2588:   if (isa<LabelStmt>(S) || isa<DefaultStmt>(S) || isa<CaseStmt>(S)) {
     105               62:     LabelAndGotoScopes[S] = ParentScope;
                     2457: branch 1 taken
                       69: branch 2 taken
                     2452: branch 4 taken
                        5: branch 5 taken
                     2450: branch 7 taken
                        2: branch 8 taken
                        2: branch 10 taken
                     2448: branch 11 taken
                       78: branch 12 taken
                     2448: branch 13 taken
     106             2526:   } else if (isa<GotoStmt>(S) || isa<SwitchStmt>(S) ||
     107                 :              isa<IndirectGotoStmt>(S) || isa<AddrLabelExpr>(S)) {
     108                 :     // Remember both what scope a goto is in as well as the fact that we have
     109                 :     // it.  This makes the second scan not have to walk the AST again.
     110               78:     LabelAndGotoScopes[S] = ParentScope;
     111               78:     Jumps.push_back(S);
     112                 :   }
     113                 : 
                     2485: branch 4 taken
                     2588: branch 5 taken
     114             5073:   for (Stmt::child_iterator CI = S->child_begin(), E = S->child_end(); CI != E;
     115                 :        ++CI) {
     116             2485:     Stmt *SubStmt = *CI;
                     2441: branch 0 taken
                       44: branch 1 taken
     117             2485:     if (SubStmt == 0) continue;
     118                 : 
     119                 :     // FIXME: diagnose jumps past initialization: required in C++, warning in C.
     120                 :     //   goto L; int X = 4;   L: ;
     121                 : 
     122                 :     // If this is a declstmt with a VLA definition, it defines a scope from here
     123                 :     // to the end of the containing context.
                      379: branch 1 taken
                     2062: branch 2 taken
     124             2441:     if (DeclStmt *DS = dyn_cast<DeclStmt>(SubStmt)) {
     125                 :       // The decl statement creates a scope if any of the decls in it are VLAs or
     126                 :       // have the cleanup attribute.
                      391: branch 2 taken
                      379: branch 3 taken
     127              770:       for (DeclStmt::decl_iterator I = DS->decl_begin(), E = DS->decl_end();
     128                 :            I != E; ++I) {
     129                 :         // If this decl causes a new scope, push and switch to it.
                      223: branch 1 taken
                      168: branch 2 taken
     130              391:         if (unsigned Diag = GetDiagForGotoScopeDecl(*I)) {
     131              223:           Scopes.push_back(GotoScope(ParentScope, Diag, (*I)->getLocation()));
     132              223:           ParentScope = Scopes.size()-1;
     133                 :         }
     134                 : 
     135                 :         // If the decl has an initializer, walk it with the potentially new
     136                 :         // scope we just installed.
                      369: branch 1 taken
                       22: branch 2 taken
     137              391:         if (VarDecl *VD = dyn_cast<VarDecl>(*I))
                      169: branch 1 taken
                      200: branch 2 taken
     138              369:           if (Expr *Init = VD->getInit())
     139              169:             BuildScopeInformation(Init, ParentScope);
     140                 :       }
     141              379:       continue;
     142                 :     }
     143                 : 
     144                 :     // Disallow jumps into any part of an @try statement by pushing a scope and
     145                 :     // walking all sub-stmts in that scope.
                       46: branch 1 taken
                     2016: branch 2 taken
     146             2062:     if (ObjCAtTryStmt *AT = dyn_cast<ObjCAtTryStmt>(SubStmt)) {
     147                 :       // Recursively walk the AST for the @try part.
     148                 :       Scopes.push_back(GotoScope(ParentScope,diag::note_protected_by_objc_try,
     149               46:                                  AT->getAtTryLoc()));
                       46: branch 1 taken
                        0: branch 2 not taken
     150               46:       if (Stmt *TryPart = AT->getTryBody())
     151               46:         BuildScopeInformation(TryPart, Scopes.size()-1);
     152                 : 
     153                 :       // Jump from the catch to the finally or try is not valid.
                       55: branch 2 taken
                       46: branch 3 taken
     154              101:       for (ObjCAtCatchStmt *AC = AT->getCatchStmts(); AC;
     155                 :            AC = AC->getNextCatchStmt()) {
     156                 :         Scopes.push_back(GotoScope(ParentScope,
     157                 :                                    diag::note_protected_by_objc_catch,
     158               55:                                    AC->getAtCatchLoc()));
     159                 :         // @catches are nested and it isn't
     160               55:         BuildScopeInformation(AC->getCatchBody(), Scopes.size()-1);
     161                 :       }
     162                 : 
     163                 :       // Jump from the finally to the try or catch is not valid.
                       21: branch 1 taken
                       25: branch 2 taken
     164               46:       if (ObjCAtFinallyStmt *AF = AT->getFinallyStmt()) {
     165                 :         Scopes.push_back(GotoScope(ParentScope,
     166                 :                                    diag::note_protected_by_objc_finally,
     167               21:                                    AF->getAtFinallyLoc()));
     168               21:         BuildScopeInformation(AF, Scopes.size()-1);
     169                 :       }
     170                 : 
     171               46:       continue;
     172                 :     }
     173                 : 
     174                 :     // Disallow jumps into the protected statement of an @synchronized, but
     175                 :     // allow jumps into the object expression it protects.
                       20: branch 1 taken
                     1996: branch 2 taken
     176             2016:     if (ObjCAtSynchronizedStmt *AS = dyn_cast<ObjCAtSynchronizedStmt>(SubStmt)){
     177                 :       // Recursively walk the AST for the @synchronized object expr, it is
     178                 :       // evaluated in the normal scope.
     179               20:       BuildScopeInformation(AS->getSynchExpr(), ParentScope);
     180                 : 
     181                 :       // Recursively walk the AST for the @synchronized part, protected by a new
     182                 :       // scope.
     183                 :       Scopes.push_back(GotoScope(ParentScope,
     184                 :                                  diag::note_protected_by_objc_synchronized,
     185               20:                                  AS->getAtSynchronizedLoc()));
     186               20:       BuildScopeInformation(AS->getSynchBody(), Scopes.size()-1);
     187               20:       continue;
     188                 :     }
     189                 : 
     190                 :     // Disallow jumps into any part of a C++ try statement. This is pretty
     191                 :     // much the same as for Obj-C.
                       17: branch 1 taken
                     1979: branch 2 taken
     192             1996:     if (CXXTryStmt *TS = dyn_cast<CXXTryStmt>(SubStmt)) {
     193                 :       Scopes.push_back(GotoScope(ParentScope, diag::note_protected_by_cxx_try,
     194               17:                                  TS->getSourceRange().getBegin()));
                       17: branch 1 taken
                        0: branch 2 not taken
     195               17:       if (Stmt *TryBlock = TS->getTryBlock())
     196               17:         BuildScopeInformation(TryBlock, Scopes.size()-1);
     197                 : 
     198                 :       // Jump from the catch into the try is not allowed either.
                       29: branch 1 taken
                       17: branch 2 taken
     199               46:       for (unsigned I = 0, E = TS->getNumHandlers(); I != E; ++I) {
     200               29:         CXXCatchStmt *CS = TS->getHandler(I);
     201                 :         Scopes.push_back(GotoScope(ParentScope,
     202                 :                                    diag::note_protected_by_cxx_catch,
     203               29:                                    CS->getSourceRange().getBegin()));
     204               29:         BuildScopeInformation(CS->getHandlerBlock(), Scopes.size()-1);
     205                 :       }
     206                 : 
     207               17:       continue;
     208                 :     }
     209                 : 
     210                 :     // Recursively walk the AST.
     211             1979:     BuildScopeInformation(SubStmt, ParentScope);
     212                 :   }
     213             2588: }
     214                 : 
     215                 : /// VerifyJumps - Verify each element of the Jumps array to see if they are
     216                 : /// valid, emitting diagnostics if not.
     217              232: void JumpScopeChecker::VerifyJumps() {
                       78: branch 1 taken
                      232: branch 2 taken
     218              542:   while (!Jumps.empty()) {
     219               78:     Stmt *Jump = Jumps.pop_back_val();
     220                 : 
     221                 :     // With a goto,
                       69: branch 1 taken
                        9: branch 2 taken
     222               78:     if (GotoStmt *GS = dyn_cast<GotoStmt>(Jump)) {
     223                 :       CheckJump(GS, GS->getLabel(), GS->getGotoLoc(),
     224               69:                 diag::err_goto_into_protected_scope);
     225               69:       continue;
     226                 :     }
     227                 : 
                        5: branch 1 taken
                        4: branch 2 taken
     228                9:     if (SwitchStmt *SS = dyn_cast<SwitchStmt>(Jump)) {
                       12: branch 2 taken
                        5: branch 3 taken
     229               34:       for (SwitchCase *SC = SS->getSwitchCaseList(); SC;
     230                 :            SC = SC->getNextSwitchCase()) {
                       12: branch 1 taken
                        0: branch 2 not taken
     231               12:         assert(LabelAndGotoScopes.count(SC) && "Case not visited?");
     232                 :         CheckJump(SS, SC, SC->getLocStart(),
     233               12:                   diag::err_switch_into_protected_scope);
     234                 :       }
     235                5:       continue;
     236                 :     }
     237                 : 
     238                 :     unsigned DiagnosticScope;
     239                 : 
     240                 :     // We don't know where an indirect goto goes, require that it be at the
     241                 :     // top level of scoping.
                        2: branch 1 taken
                        2: branch 2 taken
     242                4:     if (IndirectGotoStmt *IG = dyn_cast<IndirectGotoStmt>(Jump)) {
     243                 :       assert(LabelAndGotoScopes.count(Jump) &&
                        2: branch 1 taken
                        0: branch 2 not taken
     244                2:              "Jump didn't get added to scopes?");
     245                2:       unsigned GotoScope = LabelAndGotoScopes[IG];
                        1: branch 0 taken
                        1: branch 1 taken
     246                2:       if (GotoScope == 0) continue;  // indirect jump is ok.
     247                1:       S.Diag(IG->getGotoLoc(), diag::err_indirect_goto_in_protected_scope);
     248                1:       DiagnosticScope = GotoScope;
     249                 :     } else {
     250                 :       // We model &&Label as a jump for purposes of scope tracking.  We actually
     251                 :       // don't care *where* the address of label is, but we require the *label
     252                 :       // itself* to be in scope 0.  If it is nested inside of a VLA scope, then
     253                 :       // it is possible for an indirect goto to illegally enter the VLA scope by
     254                 :       // indirectly jumping to the label.
                        2: branch 1 taken
                        0: branch 2 not taken
     255                2:       assert(isa<AddrLabelExpr>(Jump) && "Unknown jump type");
     256                2:       LabelStmt *TheLabel = cast<AddrLabelExpr>(Jump)->getLabel();
     257                 : 
     258                 :       assert(LabelAndGotoScopes.count(TheLabel) &&
                        2: branch 1 taken
                        0: branch 2 not taken
     259                2:              "Referenced label didn't get added to scopes?");
     260                2:       unsigned LabelScope = LabelAndGotoScopes[TheLabel];
                        1: branch 0 taken
                        1: branch 1 taken
     261                2:       if (LabelScope == 0) continue; // Addr of label is ok.
     262                 : 
     263                1:       S.Diag(Jump->getLocStart(), diag::err_addr_of_label_in_protected_scope);
     264                1:       DiagnosticScope = LabelScope;
     265                 :     }
     266                 : 
     267                 :     // Report all the things that would be skipped over by this &&label or
     268                 :     // indirect goto.
                        2: branch 0 taken
                        2: branch 1 taken
     269                6:     while (DiagnosticScope != 0) {
     270                2:       S.Diag(Scopes[DiagnosticScope].Loc, Scopes[DiagnosticScope].Diag);
     271                2:       DiagnosticScope = Scopes[DiagnosticScope].ParentScope;
     272                 :     }
     273                 :   }
     274              232: }
     275                 : 
     276                 : /// CheckJump - Validate that the specified jump statement is valid: that it is
     277                 : /// jumping within or out of its current scope, not into a deeper one.
     278                 : void JumpScopeChecker::CheckJump(Stmt *From, Stmt *To,
     279               81:                                  SourceLocation DiagLoc, unsigned JumpDiag) {
                       81: branch 1 taken
                        0: branch 2 not taken
     280               81:   assert(LabelAndGotoScopes.count(From) && "Jump didn't get added to scopes?");
     281               81:   unsigned FromScope = LabelAndGotoScopes[From];
     282                 : 
                       81: branch 1 taken
                        0: branch 2 not taken
     283               81:   assert(LabelAndGotoScopes.count(To) && "Jump didn't get added to scopes?");
     284               81:   unsigned ToScope = LabelAndGotoScopes[To];
     285                 : 
     286                 :   // Common case: exactly the same scope, which is fine.
                       50: branch 0 taken
                       31: branch 1 taken
     287               81:   if (FromScope == ToScope) return;
     288                 : 
     289                 :   // The only valid mismatch jump case happens when the jump is more deeply
     290                 :   // nested inside the jump target.  Do a quick scan to see if the jump is valid
     291                 :   // because valid code is more common than invalid code.
     292               50:   unsigned TestScope = Scopes[FromScope].ParentScope;
                       29: branch 0 taken
                       42: branch 1 taken
     293              121:   while (TestScope != ~0U) {
     294                 :     // If we found the jump target, then we're jumping out of our current scope,
     295                 :     // which is perfectly fine.
                       21: branch 0 taken
                        8: branch 1 taken
     296               29:     if (TestScope == ToScope) return;
     297                 : 
     298                 :     // Otherwise, scan up the hierarchy.
     299               21:     TestScope = Scopes[TestScope].ParentScope;
     300                 :   }
     301                 : 
     302                 :   // If we get here, then we know we have invalid code.  Diagnose the bad jump,
     303                 :   // and then emit a note at each VLA being jumped out of.
     304               42:   S.Diag(DiagLoc, JumpDiag);
     305                 : 
     306                 :   // Eliminate the common prefix of the jump and the target.  Start by
     307                 :   // linearizing both scopes, reversing them as we go.
     308               42:   std::vector<unsigned> FromScopes, ToScopes;
                       63: branch 1 taken
                       42: branch 2 taken
     309              105:   for (TestScope = FromScope; TestScope != ~0U;
     310                 :        TestScope = Scopes[TestScope].ParentScope)
     311               63:     FromScopes.push_back(TestScope);
                       97: branch 1 taken
                       42: branch 2 taken
     312              139:   for (TestScope = ToScope; TestScope != ~0U;
     313                 :        TestScope = Scopes[TestScope].ParentScope)
     314               97:     ToScopes.push_back(TestScope);
     315                 : 
     316                 :   // Remove any common entries (such as the top-level function scope).
                       63: branch 1 taken
                       30: branch 2 taken
                       51: branch 5 taken
                       12: branch 6 taken
                       51: branch 7 taken
                       42: branch 8 taken
     317              135:   while (!FromScopes.empty() && FromScopes.back() == ToScopes.back()) {
     318               51:     FromScopes.pop_back();
     319               51:     ToScopes.pop_back();
     320                 :   }
     321                 : 
     322                 :   // Emit diagnostics for whatever is left in ToScopes.
                       46: branch 1 taken
                       42: branch 2 taken
     323               88:   for (unsigned i = 0, e = ToScopes.size(); i != e; ++i)
     324               88:     S.Diag(Scopes[ToScopes[i]].Loc, Scopes[ToScopes[i]].Diag);
     325                 : }
     326                 : 
     327              232: void Sema::DiagnoseInvalidJumps(Stmt *Body) {
     328              232:   (void)JumpScopeChecker(Body, *this);
     329              232: }

Generated: 2010-02-10 01:31 by zcov