diff mbox series

[committed] analyzer: early rejection of disabled warnings [PR104955]

Message ID 20220316180638.4039810-1-dmalcolm@redhat.com
State New
Headers show
Series [committed] analyzer: early rejection of disabled warnings [PR104955] | expand

Commit Message

David Malcolm March 16, 2022, 6:06 p.m. UTC
Avoid generating execution paths for warnings that are ultimately
rejected due to -Wno-analyzer-* flags.

This improves the test case from taking at least several minutes
(before I killed it) to taking under a second.

This doesn't fix the slowdown seen in PR analyzer/104955 with large
numbers of warnings when the warnings are still enabled.

Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.
Pushed to trunk as r12-7677-g7fd6e36ea9aa85.

gcc/analyzer/ChangeLog:
	PR analyzer/104955
	* diagnostic-manager.cc (get_emission_location): New.
	(diagnostic_manager::diagnostic_manager): Initialize
	m_num_disabled_diagnostics.
	(diagnostic_manager::add_diagnostic): Reject diagnostics that
	will eventually be rejected due to being disabled.
	(diagnostic_manager::emit_saved_diagnostics): Log the number
	of disabled diagnostics.
	(diagnostic_manager::emit_saved_diagnostic): Split out logic for
	determining emission location to get_emission_location.
	* diagnostic-manager.h
	(diagnostic_manager::m_num_disabled_diagnostics): New field.
	* engine.cc (stale_jmp_buf::get_controlling_option): New.
	(stale_jmp_buf::emit): Use it.
	* pending-diagnostic.h
	(pending_diagnostic::get_controlling_option): New vfunc.
	* region-model.cc
	(poisoned_value_diagnostic::get_controlling_option): New.
	(poisoned_value_diagnostic::emit): Use it.
	(shift_count_negative_diagnostic::get_controlling_option): New.
	(shift_count_negative_diagnostic::emit): Use it.
	(shift_count_overflow_diagnostic::get_controlling_option): New.
	(shift_count_overflow_diagnostic::emit): Use it.
	(dump_path_diagnostic::get_controlling_option): New.
	(dump_path_diagnostic::emit): Use it.
	(write_to_const_diagnostic::get_controlling_option): New.
	(write_to_const_diagnostic::emit): Use it.
	(write_to_string_literal_diagnostic::get_controlling_option): New.
	(write_to_string_literal_diagnostic::emit): Use it.
	* sm-file.cc (double_fclose::get_controlling_option): New.
	(double_fclose::emit): Use it.
	(file_leak::get_controlling_option): New.
	(file_leak::emit): Use it.
	* sm-malloc.cc (mismatching_deallocation::get_controlling_option):
	New.
	(mismatching_deallocation::emit): Use it.
	(double_free::get_controlling_option): New.
	(double_free::emit): Use it.
	(possible_null_deref::get_controlling_option): New.
	(possible_null_deref::emit): Use it.
	(possible_null_arg::get_controlling_option): New.
	(possible_null_arg::emit): Use it.
	(null_deref::get_controlling_option): New.
	(null_deref::emit): Use it.
	(null_arg::get_controlling_option): New.
	(null_arg::emit): Use it.
	(use_after_free::get_controlling_option): New.
	(use_after_free::emit): Use it.
	(malloc_leak::get_controlling_option): New.
	(malloc_leak::emit): Use it.
	(free_of_non_heap::get_controlling_option): New.
	(free_of_non_heap::emit): Use it.
	* sm-pattern-test.cc (pattern_match::get_controlling_option): New.
	(pattern_match::emit): Use it.
	* sm-sensitive.cc
	(exposure_through_output_file::get_controlling_option): New.
	(exposure_through_output_file::emit): Use it.
	* sm-signal.cc (signal_unsafe_call::get_controlling_option): New.
	(signal_unsafe_call::emit): Use it.
	* sm-taint.cc (tainted_array_index::get_controlling_option): New.
	(tainted_array_index::emit): Use it.
	(tainted_offset::get_controlling_option): New.
	(tainted_offset::emit): Use it.
	(tainted_size::get_controlling_option): New.
	(tainted_size::emit): Use it.
	(tainted_divisor::get_controlling_option): New.
	(tainted_divisor::emit): Use it.
	(tainted_allocation_size::get_controlling_option): New.
	(tainted_allocation_size::emit): Use it.

gcc/testsuite/ChangeLog:
	* gcc.dg/analyzer/many-disabled-diagnostics.c: New test.
	* gcc.dg/plugin/analyzer_gil_plugin.c
	(gil_diagnostic::get_controlling_option): New.
	(double_save_thread::emit): Use it.
	(fncall_without_gil::emit): Likewise.
	(pyobject_usage_without_gil::emit): Likewise.

Signed-off-by: David Malcolm <dmalcolm@redhat.com>
---
 gcc/analyzer/diagnostic-manager.cc            | 44 ++++++++++--
 gcc/analyzer/diagnostic-manager.h             |  1 +
 gcc/analyzer/engine.cc                        |  7 +-
 gcc/analyzer/pending-diagnostic.h             |  6 ++
 gcc/analyzer/region-model.cc                  | 61 ++++++++++++----
 gcc/analyzer/sm-file.cc                       | 16 ++++-
 gcc/analyzer/sm-malloc.cc                     | 72 +++++++++++++++----
 gcc/analyzer/sm-pattern-test.cc               |  8 ++-
 gcc/analyzer/sm-sensitive.cc                  |  8 ++-
 gcc/analyzer/sm-signal.cc                     |  8 ++-
 gcc/analyzer/sm-taint.cc                      | 71 +++++++++++-------
 .../analyzer/many-disabled-diagnostics.c      | 55 ++++++++++++++
 .../gcc.dg/plugin/analyzer_gil_plugin.c       | 16 +++--
 13 files changed, 300 insertions(+), 73 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/many-disabled-diagnostics.c
diff mbox series

Patch

diff --git a/gcc/analyzer/diagnostic-manager.cc b/gcc/analyzer/diagnostic-manager.cc
index 561bb18cee0..d5e5b6926cc 100644
--- a/gcc/analyzer/diagnostic-manager.cc
+++ b/gcc/analyzer/diagnostic-manager.cc
@@ -849,13 +849,28 @@  private:
   const feasibility_problem *m_feasibility_problem;
 };
 
+/* Determine the emission location for PD at STMT in FUN.  */
+
+static location_t
+get_emission_location (const gimple *stmt, function *fun,
+		       const pending_diagnostic &pd)
+{
+  location_t loc = get_stmt_location (stmt, fun);
+
+  /* Allow the pending_diagnostic to fix up the location.  */
+  loc = pd.fixup_location (loc);
+
+  return loc;
+}
+
 /* class diagnostic_manager.  */
 
 /* diagnostic_manager's ctor.  */
 
 diagnostic_manager::diagnostic_manager (logger *logger, engine *eng,
 					int verbosity)
-: log_user (logger), m_eng (eng), m_verbosity (verbosity)
+: log_user (logger), m_eng (eng), m_verbosity (verbosity),
+  m_num_disabled_diagnostics (0)
 {
 }
 
@@ -877,6 +892,25 @@  diagnostic_manager::add_diagnostic (const state_machine *sm,
      through the exploded_graph to the diagnostic.  */
   gcc_assert (enode);
 
+  /* If this warning is ultimately going to be rejected by a -Wno-analyzer-*
+     flag, reject it now.
+     We can only do this for diagnostics where we already know the stmt,
+     and thus can determine the emission location.  */
+  if (stmt)
+    {
+      location_t loc = get_emission_location (stmt, snode->m_fun, *d);
+      int option = d->get_controlling_option ();
+      if (!warning_enabled_at (loc, option))
+	{
+	  if (get_logger ())
+	    get_logger ()->log ("rejecting disabled warning %qs",
+				d->get_kind ());
+	  delete d;
+	  m_num_disabled_diagnostics++;
+	  return;
+	}
+    }
+
   saved_diagnostic *sd
     = new saved_diagnostic (sm, enode, snode, stmt, finder, var, sval,
 			    state, d, m_saved_diagnostics.length ());
@@ -1186,6 +1220,7 @@  diagnostic_manager::emit_saved_diagnostics (const exploded_graph &eg)
   LOG_SCOPE (get_logger ());
   auto_timevar tv (TV_ANALYZER_DIAGNOSTICS);
   log ("# saved diagnostics: %i", m_saved_diagnostics.length ());
+  log ("# disabled diagnostics: %i", m_num_disabled_diagnostics);
   if (get_logger ())
     {
       unsigned i;
@@ -1265,11 +1300,10 @@  diagnostic_manager::emit_saved_diagnostic (const exploded_graph &eg,
 
   emission_path.prepare_for_emission (sd.m_d);
 
-  location_t loc = get_stmt_location (sd.m_stmt, sd.m_snode->m_fun);
+  location_t loc
+    = get_emission_location (sd.m_stmt, sd.m_snode->m_fun, *sd.m_d);
 
-  /* Allow the pending_diagnostic to fix up the primary location
-     and any locations for events.  */
-  loc = sd.m_d->fixup_location (loc);
+  /* Allow the pending_diagnostic to fix up the locations of events.  */
   emission_path.fixup_locations (sd.m_d);
 
   gcc_rich_location rich_loc (loc);
diff --git a/gcc/analyzer/diagnostic-manager.h b/gcc/analyzer/diagnostic-manager.h
index e9a568fd768..34abf56d11f 100644
--- a/gcc/analyzer/diagnostic-manager.h
+++ b/gcc/analyzer/diagnostic-manager.h
@@ -179,6 +179,7 @@  private:
   engine *m_eng;
   auto_delete_vec<saved_diagnostic> m_saved_diagnostics;
   const int m_verbosity;
+  int m_num_disabled_diagnostics;
 };
 
 } // namespace ana
diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc
index 92dadea7cb6..3c5d3986352 100644
--- a/gcc/analyzer/engine.cc
+++ b/gcc/analyzer/engine.cc
@@ -1526,10 +1526,15 @@  public:
     m_setjmp_point (setjmp_point), m_stack_pop_event (NULL)
   {}
 
+  int get_controlling_option () const FINAL OVERRIDE
+  {
+    return OPT_Wanalyzer_stale_setjmp_buffer;
+  }
+
   bool emit (rich_location *richloc) FINAL OVERRIDE
   {
     return warning_at
-      (richloc, OPT_Wanalyzer_stale_setjmp_buffer,
+      (richloc, get_controlling_option (),
        "%qs called after enclosing function of %qs has returned",
        get_user_facing_name (m_longjmp_call),
        get_user_facing_name (m_setjmp_call));
diff --git a/gcc/analyzer/pending-diagnostic.h b/gcc/analyzer/pending-diagnostic.h
index 0a438fddb26..51039ea1ba5 100644
--- a/gcc/analyzer/pending-diagnostic.h
+++ b/gcc/analyzer/pending-diagnostic.h
@@ -162,6 +162,12 @@  class pending_diagnostic
  public:
   virtual ~pending_diagnostic () {}
 
+  /* Vfunc to get the command-line option used when emitting the diagnostic,
+     or zero if there is none.
+     Used by diagnostic_manager for early rejection of diagnostics (to avoid
+     having to generate feasible execution paths for them).  */
+  virtual int get_controlling_option () const = 0;
+
   /* Vfunc for emitting the diagnostic.  The rich_location will have been
      populated with a diagnostic_path.
      Return true if a diagnostic is actually emitted.  */
diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc
index 6406627db90..1bc71c02849 100644
--- a/gcc/analyzer/region-model.cc
+++ b/gcc/analyzer/region-model.cc
@@ -479,6 +479,21 @@  public:
 	    && m_src_region == other.m_src_region);
   }
 
+  int get_controlling_option () const FINAL OVERRIDE
+  {
+    switch (m_pkind)
+      {
+      default:
+	gcc_unreachable ();
+      case POISON_KIND_UNINIT:
+	return OPT_Wanalyzer_use_of_uninitialized_value;
+      case POISON_KIND_FREED:
+	return OPT_Wanalyzer_use_after_free;
+      case POISON_KIND_POPPED_STACK:
+	return OPT_Wanalyzer_use_of_pointer_in_stale_stack_frame;
+      }
+  }
+
   bool emit (rich_location *rich_loc) FINAL OVERRIDE
   {
     switch (m_pkind)
@@ -489,8 +504,7 @@  public:
 	{
 	  diagnostic_metadata m;
 	  m.add_cwe (457); /* "CWE-457: Use of Uninitialized Variable".  */
-	  return warning_meta (rich_loc, m,
-			       OPT_Wanalyzer_use_of_uninitialized_value,
+	  return warning_meta (rich_loc, m, get_controlling_option (),
 			       "use of uninitialized value %qE",
 			       m_expr);
 	}
@@ -499,8 +513,7 @@  public:
 	{
 	  diagnostic_metadata m;
 	  m.add_cwe (416); /* "CWE-416: Use After Free".  */
-	  return warning_meta (rich_loc, m,
-			       OPT_Wanalyzer_use_after_free,
+	  return warning_meta (rich_loc, m, get_controlling_option (),
 			       "use after %<free%> of %qE",
 			       m_expr);
 	}
@@ -509,8 +522,7 @@  public:
 	{
 	  /* TODO: which CWE?  */
 	  return warning_at
-	    (rich_loc,
-	     OPT_Wanalyzer_use_of_pointer_in_stale_stack_frame,
+	    (rich_loc, get_controlling_option (),
 	     "dereferencing pointer %qE to within stale stack frame",
 	     m_expr);
 	}
@@ -571,9 +583,14 @@  public:
 	    && same_tree_p (m_count_cst, other.m_count_cst));
   }
 
+  int get_controlling_option () const FINAL OVERRIDE
+  {
+    return OPT_Wanalyzer_shift_count_negative;
+  }
+
   bool emit (rich_location *rich_loc) FINAL OVERRIDE
   {
-    return warning_at (rich_loc, OPT_Wanalyzer_shift_count_negative,
+    return warning_at (rich_loc, get_controlling_option (),
 		       "shift by negative count (%qE)", m_count_cst);
   }
 
@@ -613,9 +630,14 @@  public:
 	    && same_tree_p (m_count_cst, other.m_count_cst));
   }
 
+  int get_controlling_option () const FINAL OVERRIDE
+  {
+    return OPT_Wanalyzer_shift_count_overflow;
+  }
+
   bool emit (rich_location *rich_loc) FINAL OVERRIDE
   {
-    return warning_at (rich_loc, OPT_Wanalyzer_shift_count_overflow,
+    return warning_at (rich_loc, get_controlling_option (),
 		       "shift by count (%qE) >= precision of type (%qi)",
 		       m_count_cst, m_operand_precision);
   }
@@ -1095,6 +1117,11 @@  class dump_path_diagnostic
   : public pending_diagnostic_subclass<dump_path_diagnostic>
 {
 public:
+  int get_controlling_option () const FINAL OVERRIDE
+  {
+    return 0;
+  }
+
   bool emit (rich_location *richloc) FINAL OVERRIDE
   {
     inform (richloc, "path");
@@ -2534,6 +2561,11 @@  public:
 	    && m_decl == other.m_decl);
   }
 
+  int get_controlling_option () const FINAL OVERRIDE
+  {
+    return OPT_Wanalyzer_write_to_const;
+  }
+
   bool emit (rich_location *rich_loc) FINAL OVERRIDE
   {
     auto_diagnostic_group d;
@@ -2541,15 +2573,15 @@  public:
     switch (m_reg->get_kind ())
       {
       default:
-	warned = warning_at (rich_loc, OPT_Wanalyzer_write_to_const,
+	warned = warning_at (rich_loc, get_controlling_option (),
 			     "write to %<const%> object %qE", m_decl);
 	break;
       case RK_FUNCTION:
-	warned = warning_at (rich_loc, OPT_Wanalyzer_write_to_const,
+	warned = warning_at (rich_loc, get_controlling_option (),
 			     "write to function %qE", m_decl);
 	break;
       case RK_LABEL:
-	warned = warning_at (rich_loc, OPT_Wanalyzer_write_to_const,
+	warned = warning_at (rich_loc, get_controlling_option (),
 			     "write to label %qE", m_decl);
 	break;
       }
@@ -2597,9 +2629,14 @@  public:
     return m_reg == other.m_reg;
   }
 
+  int get_controlling_option () const FINAL OVERRIDE
+  {
+    return OPT_Wanalyzer_write_to_string_literal;
+  }
+
   bool emit (rich_location *rich_loc) FINAL OVERRIDE
   {
-    return warning_at (rich_loc, OPT_Wanalyzer_write_to_string_literal,
+    return warning_at (rich_loc, get_controlling_option (),
 		       "write to string literal");
     /* Ideally we would show the location of the STRING_CST as well,
        but it is not available at this point.  */
diff --git a/gcc/analyzer/sm-file.cc b/gcc/analyzer/sm-file.cc
index 1c996f946f6..ffc2809dc57 100644
--- a/gcc/analyzer/sm-file.cc
+++ b/gcc/analyzer/sm-file.cc
@@ -157,9 +157,14 @@  public:
 
   const char *get_kind () const FINAL OVERRIDE { return "double_fclose"; }
 
+  int get_controlling_option () const FINAL OVERRIDE
+  {
+    return OPT_Wanalyzer_double_fclose;
+  }
+
   bool emit (rich_location *rich_loc) FINAL OVERRIDE
   {
-    return warning_at (rich_loc, OPT_Wanalyzer_double_fclose,
+    return warning_at (rich_loc, get_controlling_option (),
 		       "double %<fclose%> of FILE %qE",
 		       m_arg);
   }
@@ -197,6 +202,11 @@  public:
 
   const char *get_kind () const FINAL OVERRIDE { return "file_leak"; }
 
+  int get_controlling_option () const FINAL OVERRIDE
+  {
+    return OPT_Wanalyzer_file_leak;
+  }
+
   bool emit (rich_location *rich_loc) FINAL OVERRIDE
   {
     diagnostic_metadata m;
@@ -204,11 +214,11 @@  public:
        Effective Lifetime". */
     m.add_cwe (775);
     if (m_arg)
-      return warning_meta (rich_loc, m, OPT_Wanalyzer_file_leak,
+      return warning_meta (rich_loc, m, get_controlling_option (),
 			   "leak of FILE %qE",
 			   m_arg);
     else
-      return warning_meta (rich_loc, m, OPT_Wanalyzer_file_leak,
+      return warning_meta (rich_loc, m, get_controlling_option (),
 			   "leak of FILE");
   }
 
diff --git a/gcc/analyzer/sm-malloc.cc b/gcc/analyzer/sm-malloc.cc
index a5fa60d020b..4c030803e59 100644
--- a/gcc/analyzer/sm-malloc.cc
+++ b/gcc/analyzer/sm-malloc.cc
@@ -760,6 +760,11 @@  public:
     return "mismatching_deallocation";
   }
 
+  int get_controlling_option () const FINAL OVERRIDE
+  {
+    return OPT_Wanalyzer_mismatching_deallocation;
+  }
+
   bool emit (rich_location *rich_loc) FINAL OVERRIDE
   {
     auto_diagnostic_group d;
@@ -767,13 +772,13 @@  public:
     m.add_cwe (762); /* CWE-762: Mismatched Memory Management Routines.  */
     if (const deallocator *expected_dealloc
 	  = m_expected_deallocators->maybe_get_single ())
-      return warning_meta (rich_loc, m, OPT_Wanalyzer_mismatching_deallocation,
+      return warning_meta (rich_loc, m, get_controlling_option (),
 			   "%qE should have been deallocated with %qs"
 			   " but was deallocated with %qs",
 			   m_arg, expected_dealloc->m_name,
 			   m_actual_dealloc->m_name);
     else
-      return warning_meta (rich_loc, m, OPT_Wanalyzer_mismatching_deallocation,
+      return warning_meta (rich_loc, m, get_controlling_option (),
 			   "%qs called on %qE returned from a mismatched"
 			   " allocation function",
 			   m_actual_dealloc->m_name, m_arg);
@@ -834,12 +839,17 @@  public:
 
   const char *get_kind () const FINAL OVERRIDE { return "double_free"; }
 
+  int get_controlling_option () const FINAL OVERRIDE
+  {
+    return OPT_Wanalyzer_double_free;
+  }
+
   bool emit (rich_location *rich_loc) FINAL OVERRIDE
   {
     auto_diagnostic_group d;
     diagnostic_metadata m;
     m.add_cwe (415); /* CWE-415: Double Free.  */
-    return warning_meta (rich_loc, m, OPT_Wanalyzer_double_free,
+    return warning_meta (rich_loc, m, get_controlling_option (),
 			 "double-%qs of %qE", m_funcname, m_arg);
   }
 
@@ -925,13 +935,17 @@  public:
 
   const char *get_kind () const FINAL OVERRIDE { return "possible_null_deref"; }
 
+  int get_controlling_option () const FINAL OVERRIDE
+  {
+    return OPT_Wanalyzer_possible_null_dereference;
+  }
+
   bool emit (rich_location *rich_loc) FINAL OVERRIDE
   {
     /* CWE-690: Unchecked Return Value to NULL Pointer Dereference.  */
     diagnostic_metadata m;
     m.add_cwe (690);
-    return warning_meta (rich_loc, m,
-			 OPT_Wanalyzer_possible_null_dereference,
+    return warning_meta (rich_loc, m, get_controlling_option (),
 			 "dereference of possibly-NULL %qE", m_arg);
   }
 
@@ -1010,6 +1024,10 @@  public:
 	    && m_arg_idx == sub_other.m_arg_idx);
   }
 
+  int get_controlling_option () const FINAL OVERRIDE
+  {
+    return OPT_Wanalyzer_possible_null_argument;
+  }
 
   bool emit (rich_location *rich_loc) FINAL OVERRIDE
   {
@@ -1018,7 +1036,7 @@  public:
     diagnostic_metadata m;
     m.add_cwe (690);
     bool warned
-      = warning_meta (rich_loc, m, OPT_Wanalyzer_possible_null_argument,
+      = warning_meta (rich_loc, m, get_controlling_option (),
 		      "use of possibly-NULL %qE where non-null expected",
 		      m_arg);
     if (warned)
@@ -1058,13 +1076,17 @@  public:
 
   const char *get_kind () const FINAL OVERRIDE { return "null_deref"; }
 
+  int get_controlling_option () const FINAL OVERRIDE
+  {
+    return OPT_Wanalyzer_null_dereference;
+  }
+
   bool emit (rich_location *rich_loc) FINAL OVERRIDE
   {
     /* CWE-476: NULL Pointer Dereference.  */
     diagnostic_metadata m;
     m.add_cwe (476);
-    return warning_meta (rich_loc, m,
-			 OPT_Wanalyzer_null_dereference,
+    return warning_meta (rich_loc, m, get_controlling_option (),
 			 "dereference of NULL %qE", m_arg);
   }
 
@@ -1106,6 +1128,11 @@  public:
 	    && m_arg_idx == sub_other.m_arg_idx);
   }
 
+  int get_controlling_option () const FINAL OVERRIDE
+  {
+    return OPT_Wanalyzer_null_argument;
+  }
+
   bool emit (rich_location *rich_loc) FINAL OVERRIDE
   {
     /* CWE-476: NULL Pointer Dereference.  */
@@ -1115,10 +1142,10 @@  public:
 
     bool warned;
     if (zerop (m_arg))
-      warned = warning_meta (rich_loc, m, OPT_Wanalyzer_null_argument,
+      warned = warning_meta (rich_loc, m, get_controlling_option (),
 			     "use of NULL where non-null expected");
     else
-      warned = warning_meta (rich_loc, m, OPT_Wanalyzer_null_argument,
+      warned = warning_meta (rich_loc, m, get_controlling_option (),
 			     "use of NULL %qE where non-null expected",
 			     m_arg);
     if (warned)
@@ -1159,12 +1186,17 @@  public:
 
   const char *get_kind () const FINAL OVERRIDE { return "use_after_free"; }
 
+  int get_controlling_option () const FINAL OVERRIDE
+  {
+    return OPT_Wanalyzer_use_after_free;
+  }
+
   bool emit (rich_location *rich_loc) FINAL OVERRIDE
   {
     /* CWE-416: Use After Free.  */
     diagnostic_metadata m;
     m.add_cwe (416);
-    return warning_meta (rich_loc, m, OPT_Wanalyzer_use_after_free,
+    return warning_meta (rich_loc, m, get_controlling_option (),
 			 "use after %<%s%> of %qE",
 			 m_deallocator->m_name, m_arg);
   }
@@ -1248,15 +1280,20 @@  public:
 
   const char *get_kind () const FINAL OVERRIDE { return "malloc_leak"; }
 
+  int get_controlling_option () const FINAL OVERRIDE
+  {
+    return OPT_Wanalyzer_malloc_leak;
+  }
+
   bool emit (rich_location *rich_loc) FINAL OVERRIDE
   {
     diagnostic_metadata m;
     m.add_cwe (401);
     if (m_arg)
-      return warning_meta (rich_loc, m, OPT_Wanalyzer_malloc_leak,
+      return warning_meta (rich_loc, m, get_controlling_option (),
 			   "leak of %qE", m_arg);
     else
-      return warning_meta (rich_loc, m, OPT_Wanalyzer_malloc_leak,
+      return warning_meta (rich_loc, m, get_controlling_option (),
 			   "leak of %qs", "<unknown>");
   }
 
@@ -1316,6 +1353,11 @@  public:
 	    && m_freed_reg == other.m_freed_reg);
   }
 
+  int get_controlling_option () const FINAL OVERRIDE
+  {
+    return OPT_Wanalyzer_free_of_non_heap;
+  }
+
   bool emit (rich_location *rich_loc) FINAL OVERRIDE
   {
     auto_diagnostic_group d;
@@ -1330,13 +1372,13 @@  public:
       case MEMSPACE_CODE:
       case MEMSPACE_GLOBALS:
       case MEMSPACE_READONLY_DATA:
-	return warning_meta (rich_loc, m, OPT_Wanalyzer_free_of_non_heap,
+	return warning_meta (rich_loc, m, get_controlling_option (),
 			     "%<%s%> of %qE which points to memory"
 			     " not on the heap",
 			     m_funcname, m_arg);
 	break;
       case MEMSPACE_STACK:
-	return warning_meta (rich_loc, m, OPT_Wanalyzer_free_of_non_heap,
+	return warning_meta (rich_loc, m, get_controlling_option (),
 			     "%<%s%> of %qE which points to memory"
 			     " on the stack",
 			     m_funcname, m_arg);
diff --git a/gcc/analyzer/sm-pattern-test.cc b/gcc/analyzer/sm-pattern-test.cc
index 57b346bd410..68b976aeb45 100644
--- a/gcc/analyzer/sm-pattern-test.cc
+++ b/gcc/analyzer/sm-pattern-test.cc
@@ -89,9 +89,15 @@  public:
 	    && same_tree_p (m_rhs, other.m_rhs));
   }
 
+  int get_controlling_option () const FINAL OVERRIDE
+  {
+    return 0;
+  }
+
   bool emit (rich_location *rich_loc) FINAL OVERRIDE
   {
-    return warning_at (rich_loc, 0, "pattern match on %<%E %s %E%>",
+    return warning_at (rich_loc, get_controlling_option (),
+		       "pattern match on %<%E %s %E%>",
 		       m_lhs, op_symbol_code (m_op), m_rhs);
   }
 
diff --git a/gcc/analyzer/sm-sensitive.cc b/gcc/analyzer/sm-sensitive.cc
index 1720d8d0135..f40f3e8eb2f 100644
--- a/gcc/analyzer/sm-sensitive.cc
+++ b/gcc/analyzer/sm-sensitive.cc
@@ -91,13 +91,17 @@  public:
     return same_tree_p (m_arg, other.m_arg);
   }
 
+  int get_controlling_option () const FINAL OVERRIDE
+  {
+    return OPT_Wanalyzer_exposure_through_output_file;
+  }
+
   bool emit (rich_location *rich_loc) FINAL OVERRIDE
   {
     diagnostic_metadata m;
     /* CWE-532: Information Exposure Through Log Files */
     m.add_cwe (532);
-    return warning_meta (rich_loc, m,
-			 OPT_Wanalyzer_exposure_through_output_file,
+    return warning_meta (rich_loc, m, get_controlling_option (),
 			 "sensitive value %qE written to output file",
 			 m_arg);
   }
diff --git a/gcc/analyzer/sm-signal.cc b/gcc/analyzer/sm-signal.cc
index 8d3e5ff07b1..77044e10805 100644
--- a/gcc/analyzer/sm-signal.cc
+++ b/gcc/analyzer/sm-signal.cc
@@ -113,14 +113,18 @@  public:
     return m_unsafe_call == other.m_unsafe_call;
   }
 
+  int get_controlling_option () const FINAL OVERRIDE
+  {
+    return OPT_Wanalyzer_unsafe_call_within_signal_handler;
+  }
+
   bool emit (rich_location *rich_loc) FINAL OVERRIDE
   {
     auto_diagnostic_group d;
     diagnostic_metadata m;
     /* CWE-479: Signal Handler Use of a Non-reentrant Function.  */
     m.add_cwe (479);
-    if (warning_meta (rich_loc, m,
-		      OPT_Wanalyzer_unsafe_call_within_signal_handler,
+    if (warning_meta (rich_loc, m, get_controlling_option (),
 		      "call to %qD from within signal handler",
 		      m_unsafe_fndecl))
       {
diff --git a/gcc/analyzer/sm-taint.cc b/gcc/analyzer/sm-taint.cc
index a13c2fe2cfa..e2c78cdd42b 100644
--- a/gcc/analyzer/sm-taint.cc
+++ b/gcc/analyzer/sm-taint.cc
@@ -180,6 +180,11 @@  public:
 
   const char *get_kind () const FINAL OVERRIDE { return "tainted_array_index"; }
 
+  int get_controlling_option () const FINAL OVERRIDE
+  {
+    return OPT_Wanalyzer_tainted_array_index;
+  }
+
   bool emit (rich_location *rich_loc) FINAL OVERRIDE
   {
     diagnostic_metadata m;
@@ -190,19 +195,19 @@  public:
       default:
 	gcc_unreachable ();
       case BOUNDS_NONE:
-	return warning_meta (rich_loc, m, OPT_Wanalyzer_tainted_array_index,
+	return warning_meta (rich_loc, m, get_controlling_option (),
 			     "use of attacker-controlled value %qE"
 			     " in array lookup without bounds checking",
 			     m_arg);
 	break;
       case BOUNDS_UPPER:
-	return warning_meta (rich_loc, m, OPT_Wanalyzer_tainted_array_index,
+	return warning_meta (rich_loc, m, get_controlling_option (),
 			     "use of attacker-controlled value %qE"
 			     " in array lookup without checking for negative",
 			     m_arg);
 	break;
       case BOUNDS_LOWER:
-	return warning_meta (rich_loc, m, OPT_Wanalyzer_tainted_array_index,
+	return warning_meta (rich_loc, m, get_controlling_option (),
 			     "use of attacker-controlled value %qE"
 			     " in array lookup without upper-bounds checking",
 			     m_arg);
@@ -248,6 +253,11 @@  public:
 
   const char *get_kind () const FINAL OVERRIDE { return "tainted_offset"; }
 
+  int get_controlling_option () const FINAL OVERRIDE
+  {
+    return OPT_Wanalyzer_tainted_offset;
+  }
+
   bool emit (rich_location *rich_loc) FINAL OVERRIDE
   {
     diagnostic_metadata m;
@@ -259,19 +269,19 @@  public:
 	default:
 	  gcc_unreachable ();
 	case BOUNDS_NONE:
-	  return warning_meta (rich_loc, m, OPT_Wanalyzer_tainted_offset,
+	  return warning_meta (rich_loc, m, get_controlling_option (),
 			       "use of attacker-controlled value %qE as offset"
 			       " without bounds checking",
 			       m_arg);
 	  break;
 	case BOUNDS_UPPER:
-	  return warning_meta (rich_loc, m, OPT_Wanalyzer_tainted_offset,
+	  return warning_meta (rich_loc, m, get_controlling_option (),
 			       "use of attacker-controlled value %qE as offset"
 			       " without lower-bounds checking",
 			       m_arg);
 	  break;
 	case BOUNDS_LOWER:
-	  return warning_meta (rich_loc, m, OPT_Wanalyzer_tainted_offset,
+	  return warning_meta (rich_loc, m, get_controlling_option (),
 			       "use of attacker-controlled value %qE as offset"
 			       " without upper-bounds checking",
 			       m_arg);
@@ -283,17 +293,17 @@  public:
 	default:
 	  gcc_unreachable ();
 	case BOUNDS_NONE:
-	  return warning_meta (rich_loc, m, OPT_Wanalyzer_tainted_offset,
+	  return warning_meta (rich_loc, m, get_controlling_option (),
 			       "use of attacker-controlled value as offset"
 			       " without bounds checking");
 	  break;
 	case BOUNDS_UPPER:
-	  return warning_meta (rich_loc, m, OPT_Wanalyzer_tainted_offset,
+	  return warning_meta (rich_loc, m, get_controlling_option (),
 			       "use of attacker-controlled value as offset"
 			       " without lower-bounds checking");
 	  break;
 	case BOUNDS_LOWER:
-	  return warning_meta (rich_loc, m, OPT_Wanalyzer_tainted_offset,
+	  return warning_meta (rich_loc, m, get_controlling_option (),
 			       "use of attacker-controlled value as offset"
 			       " without upper-bounds checking");
 	  break;
@@ -353,6 +363,11 @@  public:
 
   const char *get_kind () const OVERRIDE { return "tainted_size"; }
 
+  int get_controlling_option () const FINAL OVERRIDE
+  {
+    return OPT_Wanalyzer_tainted_size;
+  }
+
   bool emit (rich_location *rich_loc) OVERRIDE
   {
     diagnostic_metadata m;
@@ -362,19 +377,19 @@  public:
       default:
 	gcc_unreachable ();
       case BOUNDS_NONE:
-	return warning_meta (rich_loc, m, OPT_Wanalyzer_tainted_size,
+	return warning_meta (rich_loc, m, get_controlling_option (),
 			     "use of attacker-controlled value %qE as size"
 			     " without bounds checking",
 			     m_arg);
 	break;
       case BOUNDS_UPPER:
-	return warning_meta (rich_loc, m, OPT_Wanalyzer_tainted_size,
+	return warning_meta (rich_loc, m, get_controlling_option (),
 			     "use of attacker-controlled value %qE as size"
 			     " without lower-bounds checking",
 			     m_arg);
 	break;
       case BOUNDS_LOWER:
-	return warning_meta (rich_loc, m, OPT_Wanalyzer_tainted_size,
+	return warning_meta (rich_loc, m, get_controlling_option (),
 			     "use of attacker-controlled value %qE as size"
 			     " without upper-bounds checking",
 			     m_arg);
@@ -455,18 +470,23 @@  public:
 
   const char *get_kind () const FINAL OVERRIDE { return "tainted_divisor"; }
 
+  int get_controlling_option () const FINAL OVERRIDE
+  {
+    return OPT_Wanalyzer_tainted_divisor;
+  }
+
   bool emit (rich_location *rich_loc) FINAL OVERRIDE
   {
     diagnostic_metadata m;
     /* CWE-369: "Divide By Zero".  */
     m.add_cwe (369);
     if (m_arg)
-      return warning_meta (rich_loc, m, OPT_Wanalyzer_tainted_divisor,
+      return warning_meta (rich_loc, m, get_controlling_option (),
 			   "use of attacker-controlled value %qE as divisor"
 			   " without checking for zero",
 			   m_arg);
     else
-      return warning_meta (rich_loc, m, OPT_Wanalyzer_tainted_divisor,
+      return warning_meta (rich_loc, m, get_controlling_option (),
 			   "use of attacker-controlled value as divisor"
 			   " without checking for zero");
   }
@@ -503,6 +523,11 @@  public:
     return "tainted_allocation_size";
   }
 
+  int get_controlling_option () const FINAL OVERRIDE
+  {
+    return OPT_Wanalyzer_tainted_allocation_size;
+  }
+
   bool emit (rich_location *rich_loc) FINAL OVERRIDE
   {
     diagnostic_metadata m;
@@ -515,22 +540,19 @@  public:
 	default:
 	  gcc_unreachable ();
 	case BOUNDS_NONE:
-	  return warning_meta (rich_loc, m,
-			       OPT_Wanalyzer_tainted_allocation_size,
+	  return warning_meta (rich_loc, m, get_controlling_option (),
 			       "use of attacker-controlled value %qE as"
 			       " allocation size without bounds checking",
 			       m_arg);
 	  break;
 	case BOUNDS_UPPER:
-	  return warning_meta (rich_loc, m,
-			       OPT_Wanalyzer_tainted_allocation_size,
+	  return warning_meta (rich_loc, m, get_controlling_option (),
 			       "use of attacker-controlled value %qE as"
 			       " allocation size without lower-bounds checking",
 			       m_arg);
 	  break;
 	case BOUNDS_LOWER:
-	  return warning_meta (rich_loc, m,
-			       OPT_Wanalyzer_tainted_allocation_size,
+	  return warning_meta (rich_loc, m, get_controlling_option (),
 			       "use of attacker-controlled value %qE as"
 			       " allocation size without upper-bounds checking",
 			     m_arg);
@@ -542,22 +564,19 @@  public:
 	default:
 	  gcc_unreachable ();
 	case BOUNDS_NONE:
-	  return warning_meta (rich_loc, m,
-			       OPT_Wanalyzer_tainted_allocation_size,
+	  return warning_meta (rich_loc, m, get_controlling_option (),
 			       "use of attacker-controlled value as"
 			       " allocation size without bounds"
 			       " checking");
 	  break;
 	case BOUNDS_UPPER:
-	  return warning_meta (rich_loc, m,
-			       OPT_Wanalyzer_tainted_allocation_size,
+	  return warning_meta (rich_loc, m, get_controlling_option (),
 			       "use of attacker-controlled value as"
 			       " allocation size without lower-bounds"
 			       " checking");
 	  break;
 	case BOUNDS_LOWER:
-	  return warning_meta (rich_loc, m,
-			       OPT_Wanalyzer_tainted_allocation_size,
+	  return warning_meta (rich_loc, m, get_controlling_option (),
 			       "use of attacker-controlled value as"
 			       " allocation size without upper-bounds"
 			       " checking");
diff --git a/gcc/testsuite/gcc.dg/analyzer/many-disabled-diagnostics.c b/gcc/testsuite/gcc.dg/analyzer/many-disabled-diagnostics.c
new file mode 100644
index 00000000000..48d4023946a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/many-disabled-diagnostics.c
@@ -0,0 +1,55 @@ 
+/* { dg-additional-options "-Wno-analyzer-double-free" } */
+
+#define DOUBLE_FREE()				\
+  do {						\
+    void *p = __builtin_malloc (1024);		\
+    __builtin_free (p);				\
+    __builtin_free (p);				\
+  } while (0)
+
+#define DOUBLE_FREE_x_10()			\
+  do {						\
+    DOUBLE_FREE();				\
+    DOUBLE_FREE();				\
+    DOUBLE_FREE();				\
+    DOUBLE_FREE();				\
+    DOUBLE_FREE();				\
+    DOUBLE_FREE();				\
+    DOUBLE_FREE();				\
+    DOUBLE_FREE();				\
+    DOUBLE_FREE();				\
+    DOUBLE_FREE();				\
+  } while (0)
+
+#define DOUBLE_FREE_x_100()			\
+  do {						\
+    DOUBLE_FREE_x_10();				\
+    DOUBLE_FREE_x_10();				\
+    DOUBLE_FREE_x_10();				\
+    DOUBLE_FREE_x_10();				\
+    DOUBLE_FREE_x_10();				\
+    DOUBLE_FREE_x_10();				\
+    DOUBLE_FREE_x_10();				\
+    DOUBLE_FREE_x_10();				\
+    DOUBLE_FREE_x_10();				\
+    DOUBLE_FREE_x_10();				\
+  } while (0)
+
+#define DOUBLE_FREE_x_1000()			\
+  do {						\
+    DOUBLE_FREE_x_100();			\
+    DOUBLE_FREE_x_100();			\
+    DOUBLE_FREE_x_100();			\
+    DOUBLE_FREE_x_100();			\
+    DOUBLE_FREE_x_100();			\
+    DOUBLE_FREE_x_100();			\
+    DOUBLE_FREE_x_100();			\
+    DOUBLE_FREE_x_100();			\
+    DOUBLE_FREE_x_100();			\
+    DOUBLE_FREE_x_100();			\
+  } while (0)
+
+void test_1 (void)
+{
+  DOUBLE_FREE_x_1000 (); 
+}
diff --git a/gcc/testsuite/gcc.dg/plugin/analyzer_gil_plugin.c b/gcc/testsuite/gcc.dg/plugin/analyzer_gil_plugin.c
index 61dd490436a..12d1c8d0ba2 100644
--- a/gcc/testsuite/gcc.dg/plugin/analyzer_gil_plugin.c
+++ b/gcc/testsuite/gcc.dg/plugin/analyzer_gil_plugin.c
@@ -81,6 +81,12 @@  public:
 class gil_diagnostic : public pending_diagnostic
 {
 public:
+  /* There isn't a warning ID for us to use.  */
+  int get_controlling_option () const FINAL OVERRIDE
+  {
+    return 0;
+  }
+
   location_t fixup_location (location_t loc) const FINAL OVERRIDE
   {
     /* Ideally we'd check for specific macros here, and only
@@ -133,7 +139,7 @@  class double_save_thread : public gil_diagnostic
 
   bool emit (rich_location *rich_loc) FINAL OVERRIDE
   {
-    return warning_at (rich_loc, 0,
+    return warning_at (rich_loc, get_controlling_option (),
 		       "nested usage of %qs", "Py_BEGIN_ALLOW_THREADS");
   }
 
@@ -173,14 +179,13 @@  class fncall_without_gil : public gil_diagnostic
   bool emit (rich_location *rich_loc) FINAL OVERRIDE
   {
     auto_diagnostic_group d;
-    /* There isn't a warning ID for use to use.  */
     if (m_callee_fndecl)
-      return warning_at (rich_loc, 0,
+      return warning_at (rich_loc, get_controlling_option (),
 			 "use of PyObject as argument %i of %qE"
 			 " without the GIL",
 			 m_arg_idx + 1, m_callee_fndecl);
     else
-      return warning_at (rich_loc, 0,
+      return warning_at (rich_loc, get_controlling_option (),
 			 "use of PyObject as argument %i of call"
 			 " without the GIL",
 			 m_arg_idx + 1, m_callee_fndecl);
@@ -225,8 +230,7 @@  class pyobject_usage_without_gil : public gil_diagnostic
   bool emit (rich_location *rich_loc) FINAL OVERRIDE
   {
     auto_diagnostic_group d;
-    /* There isn't a warning ID for use to use.  */
-    return warning_at (rich_loc, 0,
+    return warning_at (rich_loc, get_controlling_option (),
 		       "use of PyObject %qE without the GIL", m_expr);
   }