]> granicus.if.org Git - clang/commitdiff
When building SwitchStmts in Sema, record whether all the enum values of a switch...
authorTed Kremenek <kremenek@apple.com>
Thu, 9 Sep 2010 00:05:53 +0000 (00:05 +0000)
committerTed Kremenek <kremenek@apple.com>
Thu, 9 Sep 2010 00:05:53 +0000 (00:05 +0000)
covered by individual case statements.  Flow-based analyses may wish to consult this information,
and recording this in the AST allows us to obviate reconstructing this information later when
we build the CFG.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@113447 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/AST/Stmt.h
lib/Sema/SemaStmt.cpp
lib/Serialization/ASTReaderStmt.cpp
lib/Serialization/ASTWriterStmt.cpp

index 62a6b6463df5aa93d33abb6540e6a3cebe8d42ed..856a14c51a1d166203cb9d2aa1ba59ae438dccd8 100644 (file)
@@ -662,6 +662,11 @@ class SwitchStmt : public Stmt {
   SwitchCase *FirstCase;
   SourceLocation SwitchLoc;
 
+  /// If the SwitchStmt is a switch on an enum value, this records whether
+  /// all the enum values were covered by CaseStmts.  This value is meant to
+  /// be a hint for possible clients.
+  unsigned AllEnumCasesCovered : 1;
+
 public:
   SwitchStmt(ASTContext &C, VarDecl *Var, Expr *cond);
 
@@ -709,6 +714,19 @@ public:
     SC->setNextSwitchCase(FirstCase);
     FirstCase = SC;
   }
+
+  /// Set a flag in the SwitchStmt indicating that if the 'switch (X)' is a
+  /// switch over an enum value then all cases have been explicitly covered.
+  void setAllEnumCasesCovered() {
+    AllEnumCasesCovered = 1;
+  }
+
+  /// Returns true if the SwitchStmt is a switch of an enum value and all cases
+  /// have been explicitly covered.
+  bool isAllEnumCasesCovered() const {
+    return (bool) AllEnumCasesCovered;
+  }
+
   virtual SourceRange getSourceRange() const {
     return SourceRange(SwitchLoc, SubExprs[BODY]->getLocEnd());
   }
index 083e4dba85e3ecdd106aa727190137479321a845..fa23654e0d7d56dbd44c551d8c32bdab6e0d7389 100644 (file)
@@ -696,14 +696,14 @@ Sema::ActOnFinishSwitchStmt(SourceLocation SwitchLoc, Stmt *Switch,
     }
 
     // Check to see if switch is over an Enum and handles all of its
-    // values.  We don't need to do this if there's a default
-    // statement or if we have a constant condition.
+    // values.  We only issue a warning if there is not 'default:', but
+    // we still do the analysis to preserve this information in the AST
+    // (which can be used by flow-based analyes).
     //
-    // TODO: we might want to check whether case values are out of the
-    // enum even if we don't want to check whether all cases are handled.
     const EnumType* ET = CondTypeBeforePromotion->getAs<EnumType>();
+
     // If switch has default case, then ignore it.
-    if (!CaseListIsErroneous && !TheDefaultStmt && !HasConstantCond && ET) {
+    if (!CaseListIsErroneous  && !HasConstantCond && ET) {
       const EnumDecl *ED = ET->getDecl();
       typedef llvm::SmallVector<std::pair<llvm::APSInt, EnumConstantDecl*>, 64> EnumValsTy;
       EnumValsTy EnumVals;
@@ -723,40 +723,46 @@ Sema::ActOnFinishSwitchStmt(SourceLocation SwitchLoc, Stmt *Switch,
       std::stable_sort(EnumVals.begin(), EnumVals.end(), CmpEnumVals);
       EnumValsTy::iterator EIend =
         std::unique(EnumVals.begin(), EnumVals.end(), EqEnumVals);
-      // See which case values aren't in enum 
-      EnumValsTy::const_iterator EI = EnumVals.begin();
-      for (CaseValsTy::const_iterator CI = CaseVals.begin();
+
+      // See which case values aren't in enum.
+      // TODO: we might want to check whether case values are out of the
+      // enum even if we don't want to check whether all cases are handled.
+      if (!TheDefaultStmt) {
+       EnumValsTy::const_iterator EI = EnumVals.begin();
+       for (CaseValsTy::const_iterator CI = CaseVals.begin();
              CI != CaseVals.end(); CI++) {
-        while (EI != EIend && EI->first < CI->first)
-          EI++;
-        if (EI == EIend || EI->first > CI->first)
+         while (EI != EIend && EI->first < CI->first)
+           EI++;
+         if (EI == EIend || EI->first > CI->first)
             Diag(CI->second->getLHS()->getExprLoc(), diag::warn_not_in_enum)
               << ED->getDeclName();
-      }
-      // See which of case ranges aren't in enum
-      EI = EnumVals.begin();
-      for (CaseRangesTy::const_iterator RI = CaseRanges.begin();
+       }
+       // See which of case ranges aren't in enum
+       EI = EnumVals.begin();
+       for (CaseRangesTy::const_iterator RI = CaseRanges.begin();
              RI != CaseRanges.end() && EI != EIend; RI++) {
-        while (EI != EIend && EI->first < RI->first)
-          EI++;
+         while (EI != EIend && EI->first < RI->first)
+           EI++;
         
-        if (EI == EIend || EI->first != RI->first) {
-          Diag(RI->second->getLHS()->getExprLoc(), diag::warn_not_in_enum)
-            << ED->getDeclName();
-        }
-
-        llvm::APSInt Hi = RI->second->getRHS()->EvaluateAsInt(Context);
-        while (EI != EIend && EI->first < Hi)
-          EI++;
-        if (EI == EIend || EI->first != Hi)
-          Diag(RI->second->getRHS()->getExprLoc(), diag::warn_not_in_enum)
-            << ED->getDeclName();
+         if (EI == EIend || EI->first != RI->first) {
+           Diag(RI->second->getLHS()->getExprLoc(), diag::warn_not_in_enum)
+             << ED->getDeclName();
+         }
+
+         llvm::APSInt Hi = RI->second->getRHS()->EvaluateAsInt(Context);
+         while (EI != EIend && EI->first < Hi)
+           EI++;
+         if (EI == EIend || EI->first != Hi)
+           Diag(RI->second->getRHS()->getExprLoc(), diag::warn_not_in_enum)
+             << ED->getDeclName();
+       }
       }
-      //Check which enum vals aren't in switch
+      // Check which enum vals aren't in switch
       CaseValsTy::const_iterator CI = CaseVals.begin();
       CaseRangesTy::const_iterator RI = CaseRanges.begin();
-      EI = EnumVals.begin();
-      for (; EI != EIend; EI++) {
+      bool hasCasesNotInSwitch = false;
+
+      for (EnumValsTy::const_iterator EI = EnumVals.begin(); EI != EIend; EI++){
         //Drop unneeded case values
         llvm::APSInt CIVal;
         while (CI != CaseVals.end() && CI->first < EI->first)
@@ -765,17 +771,23 @@ Sema::ActOnFinishSwitchStmt(SourceLocation SwitchLoc, Stmt *Switch,
         if (CI != CaseVals.end() && CI->first == EI->first)
           continue;
 
-        //Drop unneeded case ranges
+        // Drop unneeded case ranges
         for (; RI != CaseRanges.end(); RI++) {
           llvm::APSInt Hi = RI->second->getRHS()->EvaluateAsInt(Context);
           if (EI->first <= Hi)
             break;
         }
 
-        if (RI == CaseRanges.end() || EI->first < RI->first)
-          Diag(CondExpr->getExprLoc(), diag::warn_missing_cases)
-            << EI->second->getDeclName();
+        if (RI == CaseRanges.end() || EI->first < RI->first) {
+         hasCasesNotInSwitch = true;
+         if (!TheDefaultStmt)
+           Diag(CondExpr->getExprLoc(), diag::warn_missing_cases)
+             << EI->second->getDeclName();
+       }
       }
+
+      if (!hasCasesNotInSwitch)
+       SS->setAllEnumCasesCovered();
     }
   }
 
index 93664452d92cd444ee974a3e015ab2c68a5debfd..9c6d4b96dd8fa90a31048abd04289a899682d329 100644 (file)
@@ -239,6 +239,9 @@ void ASTStmtReader::VisitSwitchStmt(SwitchStmt *S) {
   S->setCond(Reader.ReadSubExpr());
   S->setBody(Reader.ReadSubStmt());
   S->setSwitchLoc(SourceLocation::getFromRawEncoding(Record[Idx++]));
+  if (Record[Idx++])
+    S->setAllEnumCasesCovered();
+
   SwitchCase *PrevSC = 0;
   for (unsigned N = Record.size(); Idx != N; ++Idx) {
     SwitchCase *SC = Reader.getSwitchCaseWithID(Record[Idx]);
index 403311b8c1af8ecf41abe533059bfc9b8a11b493..6ea2c4bd0c0b8b6cd61cf9cb3ecbccabd2c8b7db 100644 (file)
@@ -233,6 +233,7 @@ void ASTStmtWriter::VisitSwitchStmt(SwitchStmt *S) {
   Writer.AddStmt(S->getCond());
   Writer.AddStmt(S->getBody());
   Writer.AddSourceLocation(S->getSwitchLoc(), Record);
+  Record.push_back(S->isAllEnumCasesCovered());
   for (SwitchCase *SC = S->getSwitchCaseList(); SC;
        SC = SC->getNextSwitchCase())
     Record.push_back(Writer.RecordSwitchCaseID(SC));