[cfe-commits] r156086 - in /cfe/trunk: docs/LanguageExtensions.html include/clang/Basic/Attr.td include/clang/Basic/DiagnosticGroups.td include/clang/Basic/DiagnosticSemaKinds.td include/clang/Sema/AttributeList.h lib/Analysis/CFG.cpp lib/Parse/ParseDeclCXX.cpp lib/Sema/AnalysisBasedWarnings.cpp lib/Sema/AttributeList.cpp lib/Sema/SemaStmtAttr.cpp test/SemaCXX/switch-implicit-fallthrough-cxx98.cpp test/SemaCXX/switch-implicit-fallthrough.cpp

Richard Smith richard-llvm at metafoo.co.uk
Thu May 3 11:27:39 PDT 2012


Author: rsmith
Date: Thu May  3 13:27:39 2012
New Revision: 156086

URL: http://llvm.org/viewvc/llvm-project?rev=156086&view=rev
Log:
Add -Wimplicit-fallthrough warning flag, which warns on fallthrough between
cases in switch statements. Also add a [[clang::fallthrough]] attribute, which
can be used to suppress the warning in the case of intentional fallthrough.

Patch by Alexander Kornienko!

The handling of C++11 attribute namespaces in this patch is temporary, and will
be replaced with a cleaner mechanism in a subsequent patch.

Added:
    cfe/trunk/test/SemaCXX/switch-implicit-fallthrough-cxx98.cpp
    cfe/trunk/test/SemaCXX/switch-implicit-fallthrough.cpp
Modified:
    cfe/trunk/docs/LanguageExtensions.html
    cfe/trunk/include/clang/Basic/Attr.td
    cfe/trunk/include/clang/Basic/DiagnosticGroups.td
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/include/clang/Sema/AttributeList.h
    cfe/trunk/lib/Analysis/CFG.cpp
    cfe/trunk/lib/Parse/ParseDeclCXX.cpp
    cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp
    cfe/trunk/lib/Sema/AttributeList.cpp
    cfe/trunk/lib/Sema/SemaStmtAttr.cpp

Modified: cfe/trunk/docs/LanguageExtensions.html
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/docs/LanguageExtensions.html?rev=156086&r1=156085&r2=156086&view=diff
==============================================================================
--- cfe/trunk/docs/LanguageExtensions.html (original)
+++ cfe/trunk/docs/LanguageExtensions.html Thu May  3 13:27:39 2012
@@ -103,6 +103,11 @@
   <li><a href="#__sync_swap">__sync_swap</a></li>
  </ul>
 </li>
+<li><a href="#non-standard-attributes">Non-standard C++11 Attributes</a>
+<ul>
+  <li><a href="#clang__fallthrough">The <tt>clang::fallthrough</tt> attribute</a></li>
+</ul>
+</li>
 <li><a href="#targetspecific">Target-Specific Extensions</a>
   <ul>
   <li><a href="#x86-specific">X86/X86-64 Language Extensions</a></li>
@@ -1497,6 +1502,55 @@
   <li><tt>__c11_atomic_fetch_xor</tt></li>
 </ul>
 
+<!-- ======================================================================= -->
+<h2 id="non-standard-attributes">Non-standard C++11 Attributes</h2>
+<!-- ======================================================================= -->
+
+<p>Clang supports one non-standard C++11 attribute. It resides in <tt>clang</tt>
+namespace.</p>
+
+<!-- ======================================================================= -->
+<h3 id="clang__fallthrough">The <tt>clang::fallthrough</tt> attribute</h3>
+<!-- ======================================================================= -->
+
+<p>The <tt>clang::fallthrough</tt> attribute is used along with
+<tt>-Wimplicit-fallthrough</tt> diagnostic to annotate intentional fall-through
+between switch labels. It can only be applied to a null statement placed in a
+point of execution between any statement and the next switch label. It is common
+to mark these places with a specific comment, but this attribute is meant to
+replace comments with a more strict annotation, which can be checked by the
+compiler. This attribute doesn't change semantics of the code and can be used
+wherever an intended fall-through occurs, but it is designed to mimic
+control-flow statements like <tt>break;</tt> so it can be placed in most places
+where <tt>break;</tt> can, but only if there are no statements on execution path
+between it and the next switch label.</p>
+<p>Here is an example:</p>
+<pre>
+// compile with -Wimplicit-fallthrough
+switch (n) {
+case 33:
+  f();
+case 44:  // warning: unannotated fall-through
+  g();
+  <b>[[clang::fallthrough]];</b>
+case 55:  // no warning
+  if (x) {
+    h();
+    break;
+  }
+  else {
+    i();
+    <b>[[clang::fallthrough]];</b>
+  }
+case 66:  // no warning
+  p();
+  <b>[[clang::fallthrough]];</b>  // warning: fallthrough annotation does not directly
+                           //          preceed case label
+  q();
+case 77:  // warning: unannotated fall-through
+  r();
+}
+</pre>
 
 <!-- ======================================================================= -->
 <h2 id="targetspecific">Target-Specific Extensions</h2>

Modified: cfe/trunk/include/clang/Basic/Attr.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/Attr.td?rev=156086&r1=156085&r2=156086&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/Attr.td (original)
+++ cfe/trunk/include/clang/Basic/Attr.td Thu May  3 13:27:39 2012
@@ -313,6 +313,11 @@
   let ASTNode = 0;
 }
 
+def FallThrough : Attr {
+  let Spellings = ["clang___fallthrough"];
+  let Subjects = [CaseStmt, DefaultStmt];
+}
+
 def FastCall : InheritableAttr {
   let Spellings = ["fastcall", "__fastcall"];
 }

Modified: cfe/trunk/include/clang/Basic/DiagnosticGroups.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticGroups.td?rev=156086&r1=156085&r2=156086&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticGroups.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticGroups.td Thu May  3 13:27:39 2012
@@ -208,6 +208,7 @@
 def CoveredSwitchDefault : DiagGroup<"covered-switch-default">;
 def SwitchEnum     : DiagGroup<"switch-enum">;
 def Switch         : DiagGroup<"switch">;
+def ImplicitFallthrough  : DiagGroup<"implicit-fallthrough">;
 def Trigraphs      : DiagGroup<"trigraphs">;
 
 def : DiagGroup<"type-limits">;

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=156086&r1=156085&r2=156086&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Thu May  3 13:27:39 2012
@@ -5246,6 +5246,31 @@
   "%0 enumeration values not handled in switch: %1, %2, %3...">,
   InGroup<Switch>;
 
+def warn_unannotated_fallthrough : Warning<
+  "unannotated fall-through between switch labels">,
+  InGroup<ImplicitFallthrough>, DefaultIgnore;
+def note_insert_fallthrough_fixit : Note<
+  "insert '[[clang::fallthrough]];' to silence this warning">,
+  InGroup<ImplicitFallthrough>, DefaultIgnore;
+def note_insert_break_fixit : Note<
+  "insert 'break;' to avoid fall-through">,
+  InGroup<ImplicitFallthrough>, DefaultIgnore;
+def err_fallthrough_attr_wrong_target : Error<
+  "clang::fallthrough attribute is only allowed on empty statements">,
+  InGroup<IgnoredAttributes>;
+def note_fallthrough_insert_semi_fixit : Note<
+  "did you forget ';'?">,
+  InGroup<IgnoredAttributes>;
+def err_fallthrough_attr_outside_switch : Error<
+  "fallthrough annotation is outside switch statement">,
+  InGroup<IgnoredAttributes>;
+def warn_fallthrough_attr_invalid_placement : Warning<
+  "fallthrough annotation does not directly precede switch label">,
+  InGroup<ImplicitFallthrough>;
+def warn_fallthrough_attr_unreachable : Warning<
+  "fallthrough annotation in unreachable code">,
+  InGroup<ImplicitFallthrough>;
+
 def warn_unreachable_default : Warning<
   "default label in switch which covers all enumeration values">,
   InGroup<CoveredSwitchDefault>, DefaultIgnore;

Modified: cfe/trunk/include/clang/Sema/AttributeList.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/AttributeList.h?rev=156086&r1=156085&r2=156086&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/AttributeList.h (original)
+++ cfe/trunk/include/clang/Sema/AttributeList.h Thu May  3 13:27:39 2012
@@ -131,7 +131,7 @@
       UsedAsTypeAttr(false), IsAvailability(false), 
       NextInPosition(0), NextInPool(0) {
     if (numArgs) memcpy(getArgsBuffer(), args, numArgs * sizeof(Expr*));
-    AttrKind = getKind(getName());
+    AttrKind = getKind(getName(), getScopeName());
   }
 
   AttributeList(IdentifierInfo *attrName, SourceRange attrRange,
@@ -152,7 +152,7 @@
     new (&getAvailabilitySlot(IntroducedSlot)) AvailabilityChange(introduced);
     new (&getAvailabilitySlot(DeprecatedSlot)) AvailabilityChange(deprecated);
     new (&getAvailabilitySlot(ObsoletedSlot)) AvailabilityChange(obsoleted);
-    AttrKind = getKind(getName());
+    AttrKind = getKind(getName(), getScopeName());
   }
 
   friend class AttributePool;
@@ -188,7 +188,7 @@
   void setUsedAsTypeAttr() { UsedAsTypeAttr = true; }
 
   Kind getKind() const { return Kind(AttrKind); }
-  static Kind getKind(const IdentifierInfo *Name);
+  static Kind getKind(const IdentifierInfo *Name, const IdentifierInfo *Scope);
 
   AttributeList *getNext() const { return NextInPosition; }
   void setNext(AttributeList *N) { NextInPosition = N; }

Modified: cfe/trunk/lib/Analysis/CFG.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/CFG.cpp?rev=156086&r1=156085&r2=156086&view=diff
==============================================================================
--- cfe/trunk/lib/Analysis/CFG.cpp (original)
+++ cfe/trunk/lib/Analysis/CFG.cpp Thu May  3 13:27:39 2012
@@ -1070,9 +1070,6 @@
     case Stmt::LambdaExprClass:
       return VisitLambdaExpr(cast<LambdaExpr>(S), asc);
 
-    case Stmt::AttributedStmtClass:
-      return Visit(cast<AttributedStmt>(S)->getSubStmt(), asc);
-
     case Stmt::MemberExprClass:
       return VisitMemberExpr(cast<MemberExpr>(S), asc);
 

Modified: cfe/trunk/lib/Parse/ParseDeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseDeclCXX.cpp?rev=156086&r1=156085&r2=156086&view=diff
==============================================================================
--- cfe/trunk/lib/Parse/ParseDeclCXX.cpp (original)
+++ cfe/trunk/lib/Parse/ParseDeclCXX.cpp Thu May  3 13:27:39 2012
@@ -2870,28 +2870,30 @@
     }
 
     bool AttrParsed = false;
-    // No scoped names are supported; ideally we could put all non-standard
-    // attributes into namespaces.
-    if (!ScopeName) {
-      switch (AttributeList::getKind(AttrName)) {
-      // No arguments
-      case AttributeList::AT_carries_dependency:
-      case AttributeList::AT_noreturn: {
-        if (Tok.is(tok::l_paren)) {
-          Diag(Tok.getLocation(), diag::err_cxx11_attribute_forbids_arguments)
-            << AttrName->getName();
-          break;
-        }
-
-        attrs.addNew(AttrName, AttrLoc, 0, AttrLoc, 0,
-                     SourceLocation(), 0, 0, false, true);
-        AttrParsed = true;
+    switch (AttributeList::getKind(AttrName, ScopeName)) {
+    // No arguments
+    case AttributeList::AT_carries_dependency:
+    // FIXME: implement generic support of attributes with C++11 syntax
+    // see Parse/ParseDecl.cpp: ParseGNUAttributes
+    case AttributeList::AT_clang___fallthrough:
+    case AttributeList::AT_noreturn: {
+      if (Tok.is(tok::l_paren)) {
+        Diag(Tok.getLocation(), diag::err_cxx11_attribute_forbids_arguments)
+          << AttrName->getName();
         break;
       }
 
-      // Silence warnings
-      default: break;
-      }
+      attrs.addNew(AttrName,
+                   SourceRange(ScopeLoc.isValid() ? ScopeLoc : AttrLoc,
+                               AttrLoc),
+                   ScopeName, ScopeLoc, 0,
+                   SourceLocation(), 0, 0, false, true);
+      AttrParsed = true;
+      break;
+    }
+
+    // Silence warnings
+    default: break;
     }
 
     // Skip the entire parameter clause, if any

Modified: cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp?rev=156086&r1=156085&r2=156086&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp (original)
+++ cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp Thu May  3 13:27:39 2012
@@ -27,6 +27,7 @@
 #include "clang/AST/StmtCXX.h"
 #include "clang/AST/EvaluatedExprVisitor.h"
 #include "clang/AST/StmtVisitor.h"
+#include "clang/AST/RecursiveASTVisitor.h"
 #include "clang/Analysis/AnalysisContext.h"
 #include "clang/Analysis/CFG.h"
 #include "clang/Analysis/Analyses/ReachableCode.h"
@@ -42,7 +43,9 @@
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/Casting.h"
 #include <algorithm>
+#include <iterator>
 #include <vector>
+#include <deque>
 
 using namespace clang;
 
@@ -522,6 +525,188 @@
   return true;
 }
 
+namespace {
+  class FallthroughMapper : public RecursiveASTVisitor<FallthroughMapper> {
+  public:
+    FallthroughMapper(Sema &S)
+      : FoundSwitchStatements(false),
+        S(S) {
+    }
+
+    bool foundSwitchStatements() const { return FoundSwitchStatements; }
+
+    void markFallthroughVisited(const AttributedStmt *Stmt) {
+      bool Found = FallthroughStmts.erase(Stmt);
+      assert(Found);
+    }
+
+    typedef llvm::SmallPtrSet<const AttributedStmt*, 8> AttrStmts;
+
+    const AttrStmts &getFallthroughStmts() const {
+      return FallthroughStmts;
+    }
+
+    bool checkFallThroughIntoBlock(const CFGBlock &B, int &AnnotatedCnt) {
+      int UnannotatedCnt = 0;
+      AnnotatedCnt = 0;
+
+      std::deque<const CFGBlock*> BlockQueue;
+
+      std::copy(B.pred_begin(), B.pred_end(), std::back_inserter(BlockQueue));
+
+      while (!BlockQueue.empty()) {
+        const CFGBlock *P = BlockQueue.front();
+        BlockQueue.pop_front();
+
+        const Stmt *Term = P->getTerminator();
+        if (Term && isa<SwitchStmt>(Term))
+          continue; // Switch statement, good.
+
+        const SwitchCase *SW = dyn_cast_or_null<SwitchCase>(P->getLabel());
+        if (SW && SW->getSubStmt() == B.getLabel() && P->begin() == P->end())
+          continue; // Previous case label has no statements, good.
+
+        if (P->pred_begin() == P->pred_end()) {  // The block is unreachable.
+          // This only catches trivially unreachable blocks.
+          for (CFGBlock::const_iterator ElIt = P->begin(), ElEnd = P->end();
+               ElIt != ElEnd; ++ElIt) {
+            if (const CFGStmt *CS = ElIt->getAs<CFGStmt>()){
+              if (const AttributedStmt *AS = asFallThroughAttr(CS->getStmt())) {
+                S.Diag(AS->getLocStart(),
+                       diag::warn_fallthrough_attr_unreachable);
+                markFallthroughVisited(AS);
+                ++AnnotatedCnt;
+              }
+              // Don't care about other unreachable statements.
+            }
+          }
+          // If there are no unreachable statements, this may be a special
+          // case in CFG:
+          // case X: {
+          //    A a;  // A has a destructor.
+          //    break;
+          // }
+          // // <<<< This place is represented by a 'hanging' CFG block.
+          // case Y:
+          continue;
+        }
+
+        const Stmt *LastStmt = getLastStmt(*P);
+        if (const AttributedStmt *AS = asFallThroughAttr(LastStmt)) {
+          markFallthroughVisited(AS);
+          ++AnnotatedCnt;
+          continue; // Fallthrough annotation, good.
+        }
+
+        if (!LastStmt) { // This block contains no executable statements.
+          // Traverse its predecessors.
+          std::copy(P->pred_begin(), P->pred_end(),
+                    std::back_inserter(BlockQueue));
+          continue;
+        }
+
+        ++UnannotatedCnt;
+      }
+      return !!UnannotatedCnt;
+    }
+
+    // RecursiveASTVisitor setup.
+    bool shouldWalkTypesOfTypeLocs() const { return false; }
+
+    bool VisitAttributedStmt(AttributedStmt *S) {
+      if (asFallThroughAttr(S))
+        FallthroughStmts.insert(S);
+      return true;
+    }
+
+    bool VisitSwitchStmt(SwitchStmt *S) {
+      FoundSwitchStatements = true;
+      return true;
+    }
+
+  private:
+
+    static const AttributedStmt *asFallThroughAttr(const Stmt *S) {
+      if (const AttributedStmt *AS = dyn_cast_or_null<AttributedStmt>(S)) {
+        if (hasSpecificAttr<FallThroughAttr>(AS->getAttrs()))
+          return AS;
+      }
+      return 0;
+    }
+
+    static const Stmt *getLastStmt(const CFGBlock &B) {
+      if (const Stmt *Term = B.getTerminator())
+        return Term;
+      for (CFGBlock::const_reverse_iterator ElemIt = B.rbegin(),
+                                            ElemEnd = B.rend();
+                                            ElemIt != ElemEnd; ++ElemIt) {
+        if (const CFGStmt *CS = ElemIt->getAs<CFGStmt>())
+          return CS->getStmt();
+      }
+      // Workaround to detect a statement thrown out by CFGBuilder:
+      //   case X: {} case Y:
+      //   case X: ; case Y:
+      if (const SwitchCase *SW = dyn_cast_or_null<SwitchCase>(B.getLabel()))
+        if (!isa<SwitchCase>(SW->getSubStmt()))
+          return SW->getSubStmt();
+
+      return 0;
+    }
+
+    bool FoundSwitchStatements;
+    AttrStmts FallthroughStmts;
+    Sema &S;
+  };
+}
+
+static void DiagnoseSwitchLabelsFallthrough(Sema &S, AnalysisDeclContext &AC) {
+  FallthroughMapper FM(S);
+  FM.TraverseStmt(AC.getBody());
+
+  if (!FM.foundSwitchStatements())
+    return;
+
+  CFG *Cfg = AC.getCFG();
+
+  if (!Cfg)
+    return;
+
+  int AnnotatedCnt;
+
+  for (CFG::reverse_iterator I = Cfg->rbegin(), E = Cfg->rend(); I != E; ++I) {
+    const CFGBlock &B = **I;
+    const Stmt *Label = B.getLabel();
+
+    if (!Label || !isa<SwitchCase>(Label))
+      continue;
+
+    if (!FM.checkFallThroughIntoBlock(B, AnnotatedCnt))
+      continue;
+
+    S.Diag(Label->getLocStart(), diag::warn_unannotated_fallthrough);
+
+    if (!AnnotatedCnt) {
+      SourceLocation L = Label->getLocStart();
+      if (L.isMacroID())
+        continue;
+      if (S.getLangOpts().CPlusPlus0x) {
+        S.Diag(L, diag::note_insert_fallthrough_fixit) <<
+          FixItHint::CreateInsertion(L, "[[clang::fallthrough]]; ");
+      }
+      S.Diag(L, diag::note_insert_break_fixit) <<
+        FixItHint::CreateInsertion(L, "break; ");
+    }
+  }
+
+  const FallthroughMapper::AttrStmts &Fallthroughs = FM.getFallthroughStmts();
+  for (FallthroughMapper::AttrStmts::const_iterator I = Fallthroughs.begin(),
+                                                    E = Fallthroughs.end();
+                                                    I != E; ++I) {
+    S.Diag((*I)->getLocStart(), diag::warn_fallthrough_attr_invalid_placement);
+  }
+
+}
+
 typedef std::pair<const Expr*, bool> UninitUse;
 
 namespace {
@@ -861,7 +1046,8 @@
       .setAlwaysAdd(Stmt::CStyleCastExprClass)
       .setAlwaysAdd(Stmt::DeclRefExprClass)
       .setAlwaysAdd(Stmt::ImplicitCastExprClass)
-      .setAlwaysAdd(Stmt::UnaryOperatorClass);
+      .setAlwaysAdd(Stmt::UnaryOperatorClass)
+      .setAlwaysAdd(Stmt::AttributedStmtClass);
   }
 
   // Construct the analysis context with the specified CFG build options.
@@ -973,6 +1159,11 @@
     }
   }
 
+  if (Diags.getDiagnosticLevel(diag::warn_unannotated_fallthrough,
+                              D->getLocStart()) != DiagnosticsEngine::Ignored) {
+    DiagnoseSwitchLabelsFallthrough(S, AC);
+  }
+
   // Collect statistics about the CFG if it was built.
   if (S.CollectStats && AC.isCFGBuilt()) {
     ++NumFunctionsAnalyzed;

Modified: cfe/trunk/lib/Sema/AttributeList.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/AttributeList.cpp?rev=156086&r1=156085&r2=156086&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/AttributeList.cpp (original)
+++ cfe/trunk/lib/Sema/AttributeList.cpp Thu May  3 13:27:39 2012
@@ -15,6 +15,7 @@
 #include "clang/AST/Expr.h"
 #include "clang/Basic/IdentifierTable.h"
 #include "llvm/ADT/StringSwitch.h"
+#include "llvm/ADT/SmallString.h"
 using namespace clang;
 
 size_t AttributeList::allocated_size() const {
@@ -99,7 +100,8 @@
 
 #include "clang/Sema/AttrParsedAttrKinds.inc"
 
-AttributeList::Kind AttributeList::getKind(const IdentifierInfo *Name) {
+AttributeList::Kind AttributeList::getKind(const IdentifierInfo *Name,
+                                           const IdentifierInfo *ScopeName) {
   StringRef AttrName = Name->getName();
 
   // Normalize the attribute name, __foo__ becomes foo.
@@ -107,5 +109,10 @@
       AttrName.size() >= 4)
     AttrName = AttrName.substr(2, AttrName.size() - 4);
 
+  // FIXME: implement attribute namespacing correctly.
+  SmallString<64> Buf;
+  if (ScopeName)
+    AttrName = ((Buf += ScopeName->getName()) += "___") += AttrName;
+
   return ::getAttrKind(AttrName);
 }

Modified: cfe/trunk/lib/Sema/SemaStmtAttr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaStmtAttr.cpp?rev=156086&r1=156085&r2=156086&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaStmtAttr.cpp (original)
+++ cfe/trunk/lib/Sema/SemaStmtAttr.cpp Thu May  3 13:27:39 2012
@@ -15,20 +15,46 @@
 #include "TargetAttributesSema.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/Basic/SourceManager.h"
+#include "clang/Lex/Lexer.h"
 #include "clang/Sema/DelayedDiagnostic.h"
 #include "clang/Sema/Lookup.h"
+#include "clang/Sema/ScopeInfo.h"
 #include "llvm/ADT/StringExtras.h"
+
 using namespace clang;
 using namespace sema;
 
+static Attr *handleFallThroughAttr(Sema &S, Stmt *St, const AttributeList &A,
+                                   SourceRange Range) {
+  if (!isa<NullStmt>(St)) {
+    S.Diag(A.getRange().getBegin(), diag::err_fallthrough_attr_wrong_target)
+        << St->getLocStart();
+    if (isa<SwitchCase>(St)) {
+      SourceLocation L = Lexer::getLocForEndOfToken(Range.getEnd(), 0,
+                                  S.getSourceManager(), S.getLangOpts());
+      S.Diag(L, diag::note_fallthrough_insert_semi_fixit)
+          << FixItHint::CreateInsertion(L, ";");
+    }
+    return 0;
+  }
+  if (S.getCurFunction()->SwitchStack.empty()) {
+    S.Diag(A.getRange().getBegin(), diag::err_fallthrough_attr_outside_switch);
+    return 0;
+  }
+  return ::new (S.Context) FallThroughAttr(A.getRange(), S.Context);
+}
+
 
-static Attr *ProcessStmtAttribute(Sema &S, Stmt *St, const AttributeList &A) {
+static Attr *ProcessStmtAttribute(Sema &S, Stmt *St, const AttributeList &A,
+                                  SourceRange Range) {
   switch (A.getKind()) {
+  case AttributeList::AT_clang___fallthrough:
+    return handleFallThroughAttr(S, St, A, Range);
   default:
     // if we're here, then we parsed an attribute, but didn't recognize it as a
     // statement attribute => it is declaration attribute
-    S.Diag(A.getRange().getBegin(), diag::warn_attribute_invalid_on_stmt) <<
-      A.getName()->getName();
+    S.Diag(A.getRange().getBegin(), diag::warn_attribute_invalid_on_stmt)
+        << A.getName()->getName() << St->getLocStart();
     return 0;
   }
 }
@@ -37,7 +63,7 @@
                                        SourceRange Range) {
   AttrVec Attrs;
   for (const AttributeList* l = AttrList; l; l = l->getNext()) {
-    if (Attr *a = ProcessStmtAttribute(*this, S, *l))
+    if (Attr *a = ProcessStmtAttribute(*this, S, *l, Range))
       Attrs.push_back(a);
   }
 

Added: cfe/trunk/test/SemaCXX/switch-implicit-fallthrough-cxx98.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/switch-implicit-fallthrough-cxx98.cpp?rev=156086&view=auto
==============================================================================
--- cfe/trunk/test/SemaCXX/switch-implicit-fallthrough-cxx98.cpp (added)
+++ cfe/trunk/test/SemaCXX/switch-implicit-fallthrough-cxx98.cpp Thu May  3 13:27:39 2012
@@ -0,0 +1,119 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++98 -Wimplicit-fallthrough %s
+
+
+int fallthrough(int n) {
+  switch (n / 10) {
+      if (n - 1) {
+        n = 100;
+      } else if (n - 2) {
+        n = 101;
+      } else if (n - 3) {
+        n = 102;
+      }
+    case -1: // expected-warning{{unannotated fall-through between switch labels}} expected-note{{insert 'break;' to avoid fall-through}}
+      ;
+    case 0: {// expected-warning{{unannotated fall-through between switch labels}} expected-note{{insert 'break;' to avoid fall-through}}
+    }
+    case 1:  // expected-warning{{unannotated fall-through between switch labels}} expected-note{{insert 'break;' to avoid fall-through}}
+      n += 100         ;
+    case 3:  // expected-warning{{unannotated fall-through between switch labels}} expected-note{{insert 'break;' to avoid fall-through}}
+      if (n > 0)
+        n += 200;
+    case 4:  // expected-warning{{unannotated fall-through between switch labels}} expected-note{{insert 'break;' to avoid fall-through}}
+      if (n < 0)
+        ;
+    case 5:  // expected-warning{{unannotated fall-through between switch labels}} expected-note{{insert 'break;' to avoid fall-through}}
+      switch (n) {
+      case 111:
+        break;
+      case 112:
+        break;
+      case 113:
+        break    ;
+      }
+    case 6:  // expected-warning{{unannotated fall-through between switch labels}} expected-note{{insert 'break;' to avoid fall-through}}
+      n += 300;
+  }
+  switch (n / 30) {
+    case 11:
+    case 12:  // no warning here, intended fall-through, no statement between labels
+      n += 1600;
+  }
+  switch (n / 40) {
+    case 13:
+      if (n % 2 == 0) {
+        return 1;
+      } else {
+        return 2;
+      }
+    case 15:  // no warning here, there's no fall-through
+      n += 3200;
+  }
+  switch (n / 50) {
+    case 17: {
+      if (n % 2 == 0) {
+        return 1;
+      } else {
+        return 2;
+      }
+    }
+    case 19: { // no warning here, there's no fall-through
+      n += 6400;
+      return 3;
+    }
+    case 21: { // no warning here, there's no fall-through
+      break;
+    }
+    case 23: // no warning here, there's no fall-through
+      n += 128000;
+      break;
+    case 25: // no warning here, there's no fall-through
+      break;
+  }
+
+  return n;
+}
+
+class ClassWithDtor {
+public:
+  ~ClassWithDtor() {}
+};
+
+void fallthrough2(int n) {
+  switch (n) {
+    case 0:
+    {
+      ClassWithDtor temp;
+      break;
+    }
+    default: // no warning here, there's no fall-through
+      break;
+  }
+}
+
+#define MY_SWITCH(X, Y, Z, U, V) switch (X) { case Y: Z; case U: V; }
+#define MY_SWITCH2(X, Y, Z) switch (X) { Y; Z; }
+#define MY_CASE(X, Y) case X: Y
+#define MY_CASE2(X, Y, U, V) case X: Y; case U: V
+
+int fallthrough_macro1(int n) {
+  MY_SWITCH(n, 13, n *= 2, 14, break)  // expected-warning{{unannotated fall-through between switch labels}}
+
+  switch (n + 1) {
+    MY_CASE(33, n += 2);
+    MY_CASE(44, break);  // expected-warning{{unannotated fall-through between switch labels}}
+    MY_CASE(55, n += 3);
+  }
+
+  switch (n + 3) {
+    MY_CASE(333, return 333);
+    MY_CASE2(444, n += 44, 4444, break);  // expected-warning{{unannotated fall-through between switch labels}}
+    MY_CASE(555, n += 33);
+  }
+
+  MY_SWITCH2(n + 4, MY_CASE(17, n *= 3), MY_CASE(19, break))  // expected-warning{{unannotated fall-through between switch labels}}
+
+  MY_SWITCH2(n + 5, MY_CASE(21, break), MY_CASE2(23, n *= 7, 25, break))  // expected-warning{{unannotated fall-through between switch labels}}
+
+  return n;
+}

Added: cfe/trunk/test/SemaCXX/switch-implicit-fallthrough.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/switch-implicit-fallthrough.cpp?rev=156086&view=auto
==============================================================================
--- cfe/trunk/test/SemaCXX/switch-implicit-fallthrough.cpp (added)
+++ cfe/trunk/test/SemaCXX/switch-implicit-fallthrough.cpp Thu May  3 13:27:39 2012
@@ -0,0 +1,177 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 -Wimplicit-fallthrough %s
+
+
+int fallthrough(int n) {
+  switch (n / 10) {
+      if (n - 1) {
+        n = 100;
+      } else if (n - 2) {
+        n = 101;
+      } else if (n - 3) {
+        n = 102;
+      }
+    case -1: // expected-warning{{unannotated fall-through between switch labels}} expected-note{{insert '[[clang::fallthrough]];' to silence this warning}} expected-note{{insert 'break;' to avoid fall-through}}
+      ;
+    case 0: {// expected-warning{{unannotated fall-through between switch labels}} expected-note{{insert '[[clang::fallthrough]];' to silence this warning}} expected-note{{insert 'break;' to avoid fall-through}}
+    }
+    case 1:  // expected-warning{{unannotated fall-through between switch labels}} expected-note{{insert '[[clang::fallthrough]];' to silence this warning}} expected-note{{insert 'break;' to avoid fall-through}}
+      n += 100         ;
+    case 3:  // expected-warning{{unannotated fall-through between switch labels}} expected-note{{insert '[[clang::fallthrough]];' to silence this warning}} expected-note{{insert 'break;' to avoid fall-through}}
+      if (n > 0)
+        n += 200;
+    case 4:  // expected-warning{{unannotated fall-through between switch labels}} expected-note{{insert '[[clang::fallthrough]];' to silence this warning}} expected-note{{insert 'break;' to avoid fall-through}}
+      if (n < 0)
+        ;
+    case 5:  // expected-warning{{unannotated fall-through between switch labels}} expected-note{{insert '[[clang::fallthrough]];' to silence this warning}} expected-note{{insert 'break;' to avoid fall-through}}
+      switch (n) {
+      case 111:
+        break;
+      case 112:
+        break;
+      case 113:
+        break    ;
+      }
+    case 6:  // expected-warning{{unannotated fall-through between switch labels}} expected-note{{insert '[[clang::fallthrough]];' to silence this warning}} expected-note{{insert 'break;' to avoid fall-through}}
+      n += 300;
+  }
+  switch (n / 20) {
+    case 7:
+      n += 400;
+      [[clang::fallthrough]];
+    case 9:  // no warning here, intended fall-through marked with an attribute
+      n += 800;
+      [[clang::fallthrough]];
+    default: { // no warning here, intended fall-through marked with an attribute
+      if (n % 2 == 0) {
+        return 1;
+      } else {
+        [[clang::fallthrough]];
+      }
+    }
+    case 10:  // no warning here, intended fall-through marked with an attribute
+      if (n % 3 == 0) {
+        n %= 3;
+      } else {
+        [[clang::fallthrough]];
+      }
+    case 110:  // expected-warning{{unannotated fall-through between switch labels}} but no fix-it hint as we have one fall-through annotation!
+      n += 800;
+  }
+  switch (n / 30) {
+    case 11:
+    case 12:  // no warning here, intended fall-through, no statement between labels
+      n += 1600;
+  }
+  switch (n / 40) {
+    case 13:
+      if (n % 2 == 0) {
+        return 1;
+      } else {
+        return 2;
+      }
+    case 15:  // no warning here, there's no fall-through
+      n += 3200;
+  }
+  switch (n / 50) {
+    case 17: {
+      if (n % 2 == 0) {
+        return 1;
+      } else {
+        return 2;
+      }
+    }
+    case 19: { // no warning here, there's no fall-through
+      n += 6400;
+      return 3;
+    }
+    case 21: { // no warning here, there's no fall-through
+      break;
+    }
+    case 23: // no warning here, there's no fall-through
+      n += 128000;
+      break;
+    case 25: // no warning here, there's no fall-through
+      break;
+  }
+
+  return n;
+}
+
+class ClassWithDtor {
+public:
+  ~ClassWithDtor() {}
+};
+
+void fallthrough2(int n) {
+  switch (n) {
+    case 0:
+    {
+      ClassWithDtor temp;
+      break;
+    }
+    default: // no warning here, there's no fall-through
+      break;
+  }
+}
+
+#define MY_SWITCH(X, Y, Z, U, V) switch (X) { case Y: Z; case U: V; }
+#define MY_SWITCH2(X, Y, Z) switch (X) { Y; Z; }
+#define MY_CASE(X, Y) case X: Y
+#define MY_CASE2(X, Y, U, V) case X: Y; case U: V
+
+int fallthrough_macro1(int n) {
+  MY_SWITCH(n, 13, n *= 2, 14, break)  // expected-warning{{unannotated fall-through between switch labels}}
+
+  switch (n + 1) {
+    MY_CASE(33, n += 2);
+    MY_CASE(44, break);  // expected-warning{{unannotated fall-through between switch labels}}
+    MY_CASE(55, n += 3);
+  }
+
+  switch (n + 3) {
+    MY_CASE(333, return 333);
+    MY_CASE2(444, n += 44, 4444, break);  // expected-warning{{unannotated fall-through between switch labels}}
+    MY_CASE(555, n += 33);
+  }
+
+  MY_SWITCH2(n + 4, MY_CASE(17, n *= 3), MY_CASE(19, break))  // expected-warning{{unannotated fall-through between switch labels}}
+
+  MY_SWITCH2(n + 5, MY_CASE(21, break), MY_CASE2(23, n *= 7, 25, break))  // expected-warning{{unannotated fall-through between switch labels}}
+
+  return n;
+}
+
+int fallthrough_position(int n) {
+  switch (n) {
+      [[clang::fallthrough]];  // expected-warning{{fallthrough annotation in unreachable code}}
+    case 221:
+      [[clang::fallthrough]]; // expected-warning{{fallthrough annotation does not directly precede switch label}}
+      return 1;
+      [[clang::fallthrough]];  // expected-warning{{fallthrough annotation in unreachable code}}
+    case 222:
+      [[clang::fallthrough]]; // expected-warning{{fallthrough annotation does not directly precede switch label}}
+      n += 400;
+    case 223:          // expected-warning{{unannotated fall-through between switch labels}} expected-note{{insert '[[clang::fallthrough]];' to silence this warning}} expected-note{{insert 'break;' to avoid fall-through}}
+      [[clang::fallthrough]]; // expected-warning{{fallthrough annotation does not directly precede switch label}}
+  }
+  return n;
+}
+
+int fallthrough_targets(int n) {
+  [[clang::fallthrough]]; // expected-error{{fallthrough annotation is outside switch statement}}
+
+  [[clang::fallthrough]]  // expected-error{{fallthrough attribute is only allowed on empty statements}}
+  switch (n) {
+    case 121:
+      n += 400;
+      [[clang::fallthrough]]; // no warning here, correct target
+    case 123:
+      [[clang::fallthrough]]  // expected-error{{fallthrough attribute is only allowed on empty statements}}
+      n += 800;
+      break;
+    [[clang::fallthrough]]    // expected-error{{fallthrough attribute is only allowed on empty statements}} expected-note{{did you forget ';'?}}
+    case 125:
+      n += 1600;
+  }
+  return n;
+}





More information about the cfe-commits mailing list