Implementation of a "soft opt-in" option for -Wimplicit-fallthrough diagnostics: -Wimplicit-fallthrough-per-method


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@157871 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/include/clang/Basic/DiagnosticGroups.td b/include/clang/Basic/DiagnosticGroups.td
index 76f1e41..f48eee4 100644
--- a/include/clang/Basic/DiagnosticGroups.td
+++ b/include/clang/Basic/DiagnosticGroups.td
@@ -209,7 +209,9 @@
 def CoveredSwitchDefault : DiagGroup<"covered-switch-default">;
 def SwitchEnum     : DiagGroup<"switch-enum">;
 def Switch         : DiagGroup<"switch">;
-def ImplicitFallthrough  : DiagGroup<"implicit-fallthrough">;
+def ImplicitFallthroughPerMethod : DiagGroup<"implicit-fallthrough-per-method">;
+def ImplicitFallthrough  : DiagGroup<"implicit-fallthrough",
+                                     [ImplicitFallthroughPerMethod]>;
 def Trigraphs      : DiagGroup<"trigraphs">;
 
 def : DiagGroup<"type-limits">;
diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td
index 32576a3..d3d8238 100644
--- a/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/include/clang/Basic/DiagnosticSemaKinds.td
@@ -5297,6 +5297,9 @@
 def warn_unannotated_fallthrough : Warning<
   "unannotated fall-through between switch labels">,
   InGroup<ImplicitFallthrough>, DefaultIgnore;
+def warn_unannotated_fallthrough_per_method : Warning<
+  "unannotated fall-through between switch labels in partly annotated method">,
+  InGroup<ImplicitFallthroughPerMethod>, DefaultIgnore;
 def note_insert_fallthrough_fixit : Note<
   "insert '[[clang::fallthrough]];' to silence this warning">;
 def note_insert_break_fixit : Note<
diff --git a/lib/Sema/AnalysisBasedWarnings.cpp b/lib/Sema/AnalysisBasedWarnings.cpp
index 8f16e70..cea8a76 100644
--- a/lib/Sema/AnalysisBasedWarnings.cpp
+++ b/lib/Sema/AnalysisBasedWarnings.cpp
@@ -814,13 +814,17 @@
   };
 }
 
-static void DiagnoseSwitchLabelsFallthrough(Sema &S, AnalysisDeclContext &AC) {
+static void DiagnoseSwitchLabelsFallthrough(Sema &S, AnalysisDeclContext &AC,
+                                            bool PerMethod) {
   FallthroughMapper FM(S);
   FM.TraverseStmt(AC.getBody());
 
   if (!FM.foundSwitchStatements())
     return;
 
+  if (PerMethod && FM.getFallthroughStmts().empty())
+    return;
+
   CFG *Cfg = AC.getCFG();
 
   if (!Cfg)
@@ -838,7 +842,9 @@
     if (!FM.checkFallThroughIntoBlock(B, AnnotatedCnt))
       continue;
 
-    S.Diag(Label->getLocStart(), diag::warn_unannotated_fallthrough);
+    S.Diag(Label->getLocStart(),
+        PerMethod ? diag::warn_unannotated_fallthrough_per_method
+                  : diag::warn_unannotated_fallthrough);
 
     if (!AnnotatedCnt) {
       SourceLocation L = Label->getLocStart();
@@ -1324,9 +1330,14 @@
     }
   }
 
-  if (Diags.getDiagnosticLevel(diag::warn_unannotated_fallthrough,
-                              D->getLocStart()) != DiagnosticsEngine::Ignored) {
-    DiagnoseSwitchLabelsFallthrough(S, AC);
+  bool FallThroughDiagFull =
+      Diags.getDiagnosticLevel(diag::warn_unannotated_fallthrough,
+                               D->getLocStart()) != DiagnosticsEngine::Ignored;
+  bool FallThroughDiagPerMethod =
+      Diags.getDiagnosticLevel(diag::warn_unannotated_fallthrough_per_method,
+                               D->getLocStart()) != DiagnosticsEngine::Ignored;
+  if (FallThroughDiagFull || FallThroughDiagPerMethod) {
+    DiagnoseSwitchLabelsFallthrough(S, AC, !FallThroughDiagFull);
   }
 
   // Collect statistics about the CFG if it was built.
diff --git a/test/SemaCXX/switch-implicit-fallthrough-per-method.cpp b/test/SemaCXX/switch-implicit-fallthrough-per-method.cpp
new file mode 100644
index 0000000..3ca8590
--- /dev/null
+++ b/test/SemaCXX/switch-implicit-fallthrough-per-method.cpp
@@ -0,0 +1,39 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 -Wimplicit-fallthrough-per-method %s
+
+
+int fallthrough(int n) {
+  switch (n / 10) {
+    case 0:
+      n += 100;
+    case 1:  // expected-warning{{unannotated fall-through between switch labels in partly annotated method}} expected-note{{insert '[[clang::fallthrough]];' to silence this warning}} expected-note{{insert 'break;' to avoid fall-through}}
+      switch (n) {
+      case 111:
+        n += 111;
+        [[clang::fallthrough]];
+      case 112:
+        n += 112;
+      case 113:  // expected-warning{{unannotated fall-through between switch labels in partly annotated method}} expected-note{{insert '[[clang::fallthrough]];' to silence this warning}} expected-note{{insert 'break;' to avoid fall-through}}
+        n += 113;
+        break    ;
+      }
+  }
+  return n;
+}
+
+int fallthrough2(int n) {
+  switch (n / 10) {
+    case 0:
+      n += 100;
+    case 1:  // no warning, as we didn't "opt-in" for it in this method
+      switch (n) {
+      case 111:
+        n += 111;
+      case 112:  // no warning, as we didn't "opt-in" for it in this method
+        n += 112;
+      case 113:  // no warning, as we didn't "opt-in" for it in this method
+        n += 113;
+        break    ;
+      }
+  }
+  return n;
+}