diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc
index 8c39bb4a76b..8bb87da84c7 100644
--- a/gcc/cp/class.cc
+++ b/gcc/cp/class.cc
@@ -3243,10 +3243,15 @@ warn_hidden (tree t)
 	  continue;
 
 	tree name = OVL_NAME (fns);
+	size_t num_fns = 0; /* The number of fndecls in fns.  */
 	auto_vec<tree, 20> base_fndecls;
 	tree base_binfo;
 	tree binfo;
 	unsigned j;
+	hash_set<tree> overriden_base_fndecls, overrider_fndecls;
+	/* base_fndecls that are hidden but not overriden. The "value"
+	   contains a vector of fndecls that hide the "key".  */
+	hash_map<tree, auto_vec<tree> > hidden_base_fndecls;
 
 	if (IDENTIFIER_CDTOR_P (name))
 	  continue;
@@ -3264,47 +3269,69 @@ warn_hidden (tree t)
 	if (base_fndecls.is_empty ())
 	  continue;
 
-	/* Remove any overridden functions.  */
-	bool seen_non_override = false;
+	/* Find all the base_fndecls that are overridden, as well as those
+	   that are hidden, in T.  */
 	for (tree fndecl : ovl_range (fns))
 	  {
-	    bool any_override = false;
-	    if (TREE_CODE (fndecl) == FUNCTION_DECL
-		&& DECL_VINDEX (fndecl))
+	    fndecl = STRIP_TEMPLATE (fndecl);
+	    if (TREE_CODE (fndecl) != FUNCTION_DECL
+		|| fndecl == conv_op_marker)
+	      continue;
+	    num_fns++;
+	    for (size_t k = 0; k < base_fndecls.length (); k++)
 	      {
-		/* If the method from the base class has the same
-		   signature as the method from the derived class, it
-		   has been overridden.  Note that we can't move on
-		   after finding one match: fndecl might override
-		   multiple base fns.  */
-		for (size_t k = 0; k < base_fndecls.length (); k++)
-		  if (base_fndecls[k]
-		      && same_signature_p (fndecl, base_fndecls[k]))
-		    {
-		      base_fndecls[k] = NULL_TREE;
-		      any_override = true;
-		    }
+		if (!base_fndecls[k] || !DECL_VINDEX (base_fndecls[k]))
+		  continue;
+		if (IDENTIFIER_CONV_OP_P (name)
+		    && !same_type_p (DECL_CONV_FN_TYPE (fndecl),
+				     DECL_CONV_FN_TYPE (base_fndecls[k])))
+		  /* If base_fndecl[k] and fndecl are conversion operators
+		     to different types, they're unrelated.  */
+		  ;
+		else if (same_signature_p (fndecl, base_fndecls[k])
+			 && overrides_p (fndecl, base_fndecls[k]))
+		  {
+		    /* We cannot simply compare DECL_VINDEX (it won't work if
+		       there's multiple inheritance, or single inheritance with
+		       bases with bases), so we determine whether fndecl
+		       overrides base_fndecls[k] by first comparing the
+		       signatures and if they match, using the more expensive
+		       is_overrider function.  */
+		    overriden_base_fndecls.add (base_fndecls[k]);
+		    overrider_fndecls.add (fndecl);
+		  }
+		else
+		  {
+		    /* fndecls hides base_fndecls[k].  */
+		    auto_vec<tree> &hiders =
+		      hidden_base_fndecls.get_or_insert (base_fndecls[k]);
+		    if (!hiders.contains (fndecl))
+		      hiders.safe_push (fndecl);
+		  }
 	      }
-	    if (!any_override)
-	      seen_non_override = true;
 	  }
 
-	if (!seen_non_override && warn_overloaded_virtual == 1)
-	  /* All the derived fns override base virtuals.  */
-	  return;
+	if (warn_overloaded_virtual == 1
+	    && overrider_fndecls.elements () == num_fns)
+	  /* All the fns override a base virtual.  */
+	  continue;
 
-	/* Now give a warning for all base functions without overriders,
-	   as they are hidden.  */
-	for (tree base_fndecl : base_fndecls)
-	  if (base_fndecl)
-	    {
-	      auto_diagnostic_group d;
-	      /* Here we know it is a hider, and no overrider exists.  */
-	      if (warning_at (location_of (base_fndecl),
-			      OPT_Woverloaded_virtual_,
-			      "%qD was hidden", base_fndecl))
-		inform (location_of (fns), "  by %qD", fns);
-	    }
+	/* Now give a warning for all hidden methods. Note that a method that
+	   is both in hidden_base_fndecls and overriden_base_fndecls is not
+	   hidden.  */
+	for (auto hidden_base_fndecl : hidden_base_fndecls)
+	  {
+	    tree hidden_fndecl = hidden_base_fndecl.first;
+	    if (!hidden_fndecl
+		|| overriden_base_fndecls.contains (hidden_fndecl))
+	      continue;
+	    auto_diagnostic_group d;
+	    if (warning_at (location_of (hidden_fndecl),
+			    OPT_Woverloaded_virtual_,
+			    "%qD was hidden", hidden_fndecl))
+	      for (auto h : hidden_base_fndecl.second)
+		inform (location_of (h), "  by %qD", h);
+	  }
       }
 }
 
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 94ee550bd9c..56d72d707c8 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -7744,6 +7744,7 @@ extern tree type_context_for_name_lookup	(tree);
 extern tree lookup_conversions			(tree);
 extern tree binfo_from_vbase			(tree);
 extern tree binfo_for_vbase			(tree, tree);
+extern bool overrides_p				(tree, tree);
 extern tree look_for_overrides_here		(tree, tree);
 #define dfs_skip_bases ((tree)1)
 extern tree dfs_walk_all (tree, tree (*) (tree, void *),
diff --git a/gcc/cp/error.cc b/gcc/cp/error.cc
index 65f70c595cf..3d0dad4ee59 100644
--- a/gcc/cp/error.cc
+++ b/gcc/cp/error.cc
@@ -3397,7 +3397,8 @@ location_of (tree t)
 	return input_location;
     }
   else if (TREE_CODE (t) == OVERLOAD)
-    t = OVL_FIRST (t);
+    t = OVL_FIRST (t) != conv_op_marker ? OVL_FIRST (t)
+      : OVL_FIRST (OVL_CHAIN (t));
 
   if (DECL_P (t))
     return DECL_SOURCE_LOCATION (t);
diff --git a/gcc/cp/search.cc b/gcc/cp/search.cc
index b0421ee0524..432c919a1e9 100644
--- a/gcc/cp/search.cc
+++ b/gcc/cp/search.cc
@@ -2014,10 +2014,12 @@ maybe_check_overriding_exception_spec (tree overrider, tree basefn)
 }
 
 /* Check that virtual overrider OVERRIDER is acceptable for base function
-   BASEFN. Issue diagnostic, and return zero, if unacceptable.  */
+   BASEFN. Issue diagnostic if COMPLAIN & TF_ERROR, and return zero, if
+   unacceptable.  */
 
 static int
-check_final_overrider (tree overrider, tree basefn)
+check_final_overrider (tree overrider, tree basefn,
+		       tsubst_flags_t complain = tf_error)
 {
   tree over_type = TREE_TYPE (overrider);
   tree base_type = TREE_TYPE (basefn);
@@ -2080,8 +2082,10 @@ check_final_overrider (tree overrider, tree basefn)
 	   converting to void *, or qualification conversion.  */
 	{
 	  auto_diagnostic_group d;
-	  if (pedwarn (DECL_SOURCE_LOCATION (overrider), 0,
-		       "invalid covariant return type for %q#D", overrider))
+	  if ((complain & tf_error)
+	      && pedwarn (DECL_SOURCE_LOCATION (overrider), 0,
+			  "invalid covariant return type for %q#D",
+			  overrider))
 	    inform (DECL_SOURCE_LOCATION (basefn),
 		    "overridden function is %q#D", basefn);
 	}
@@ -2095,6 +2099,8 @@ check_final_overrider (tree overrider, tree basefn)
   else
     {
       auto_diagnostic_group d;
+      if (!(complain & tf_error))
+	return 0;
       if (fail == 1)
 	error ("invalid covariant return type for %q+#D", overrider);
       else
@@ -2114,6 +2120,8 @@ check_final_overrider (tree overrider, tree basefn)
       && !tx_safe_fn_type_p (base_type)
       && !tx_safe_fn_type_p (over_type))
     {
+      if (!(complain & tf_error))
+	return 0;
       auto_diagnostic_group d;
       error ("conflicting type attributes specified for %q+#D", overrider);
       inform (DECL_SOURCE_LOCATION (basefn),
@@ -2128,6 +2136,8 @@ check_final_overrider (tree overrider, tree basefn)
   if (DECL_IMMEDIATE_FUNCTION_P (overrider)
       != DECL_IMMEDIATE_FUNCTION_P (basefn))
     {
+      if (!(complain & tf_error))
+	return 0;
       auto_diagnostic_group d;
       if (DECL_IMMEDIATE_FUNCTION_P (overrider))
 	error ("%<consteval%> function %q+D overriding non-%<consteval%> "
@@ -2148,7 +2158,8 @@ check_final_overrider (tree overrider, tree basefn)
       && lookup_attribute ("transaction_safe_dynamic",
 			   DECL_ATTRIBUTES (overrider))
       && !lookup_attribute ("transaction_safe_dynamic",
-			    DECL_ATTRIBUTES (basefn)))
+			    DECL_ATTRIBUTES (basefn))
+      && (complain & tf_error))
     {
       auto_diagnostic_group d;
       error_at (DECL_SOURCE_LOCATION (overrider),
@@ -2159,6 +2170,8 @@ check_final_overrider (tree overrider, tree basefn)
 
   if (DECL_DELETED_FN (basefn) != DECL_DELETED_FN (overrider))
     {
+      if (!(complain & tf_error))
+	return 0;
       if (DECL_DELETED_FN (overrider))
 	{
 	  auto_diagnostic_group d;
@@ -2181,6 +2194,8 @@ check_final_overrider (tree overrider, tree basefn)
 
   if (!DECL_HAS_CONTRACTS_P (basefn) && DECL_HAS_CONTRACTS_P (overrider))
     {
+      if (!(complain & tf_error))
+	return 0;
       auto_diagnostic_group d;
       error ("function with contracts %q+D overriding contractless function",
 	     overrider);
@@ -2207,6 +2222,8 @@ check_final_overrider (tree overrider, tree basefn)
 
   if (DECL_FINAL_P (basefn))
     {
+      if (!(complain & tf_error))
+	return 0;
       auto_diagnostic_group d;
       error ("virtual function %q+D overriding final function", overrider);
       inform (DECL_SOURCE_LOCATION (basefn),
@@ -2216,6 +2233,15 @@ check_final_overrider (tree overrider, tree basefn)
   return 1;
 }
 
+/* Check whether FN is a valid overrider for base function BASEFN. Note that
+   no diagnostic is emitted if FN is not a valid overrider.  */
+
+bool
+overrides_p (tree fn, tree basefn)
+{
+  return check_final_overrider (fn, basefn, /*complain=*/tf_none);
+}
+
 /* Given a class TYPE, and a function decl FNDECL, look for
    virtual functions in TYPE's hierarchy which FNDECL overrides.
    We do not look in TYPE itself, only its bases.
diff --git a/gcc/testsuite/g++.dg/warn/Woverloaded-virt1.C b/gcc/testsuite/g++.dg/warn/Woverloaded-virt1.C
index 92f8327b9d0..9091bfabc96 100644
--- a/gcc/testsuite/g++.dg/warn/Woverloaded-virt1.C
+++ b/gcc/testsuite/g++.dg/warn/Woverloaded-virt1.C
@@ -5,10 +5,12 @@ class Foo
 {
 public:
     virtual void f(int);	// { dg-warning "hidden" }
+    void g(int);		// Not virtual, so no warning
 };
 
 class Bar : public Foo
 {
 public:
     virtual void f(short);	// { dg-message "by" }
+    virtual void g(short);
 };
diff --git a/gcc/testsuite/g++.dg/warn/Woverloaded-virt10.C b/gcc/testsuite/g++.dg/warn/Woverloaded-virt10.C
new file mode 100644
index 00000000000..42b8b0f9788
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Woverloaded-virt10.C
@@ -0,0 +1,11 @@
+// PR c++/117114 - Test case from PR c++/117114
+// { dg-do compile { target c++11 } }
+// { dg-additional-options -Woverloaded-virtual }
+
+struct Troops { virtual ~Troops(); };
+struct Control { virtual int GetControl() const = 0; };
+struct Army : Troops, Control { int GetControl() const override; };
+
+struct VirtualControl : virtual Control {
+  int GetControl() const override;
+};
diff --git a/gcc/testsuite/g++.dg/warn/Woverloaded-virt11.C b/gcc/testsuite/g++.dg/warn/Woverloaded-virt11.C
new file mode 100644
index 00000000000..bd82ce90ea8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Woverloaded-virt11.C
@@ -0,0 +1,25 @@
+// PR c++/109918 - More tests with multiple inheritance
+// { dg-do compile { target c++11 } }
+// { dg-additional-options -Woverloaded-virtual }
+
+struct Troops { virtual ~Troops(); };
+struct Control {
+  virtual int GetControl() const { return 42; } // { dg-warning "was hidden" }
+};
+struct Army : Troops, Control {
+  template<class T> void GetControl() const; // { dg-message "by" }
+};
+
+
+struct A {
+  virtual void get() const;
+};
+struct B {
+  virtual void get() const;
+  virtual void get(char) const; // { dg-warning "was hidden" }
+};
+
+struct C : A, B {
+  virtual void get() const; // { dg-message "by" }
+  virtual void get(int) const; // { dg-message "by" }
+};
diff --git a/gcc/testsuite/g++.dg/warn/Woverloaded-virt12.C b/gcc/testsuite/g++.dg/warn/Woverloaded-virt12.C
new file mode 100644
index 00000000000..3cfe22c03ec
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Woverloaded-virt12.C
@@ -0,0 +1,23 @@
+// PR c++/109918 - Test covariant return types
+// { dg-do compile { target c++11 } }
+// { dg-additional-options -Woverloaded-virtual }
+
+struct Big { virtual ~Big () {} };
+struct Smaller : Big {};
+
+// Single inheritance case
+struct Foo {
+  virtual Big* getMe() const;
+};
+struct Bar : Foo {
+  virtual Smaller* getMe() const;
+};
+
+// Multiple inheritance case
+struct Troops { virtual ~Troops(); };
+struct Control {
+  virtual Big* GetControl() const;
+};
+struct Army : Troops, Control {
+  virtual Smaller* GetControl() const;
+};
diff --git a/gcc/testsuite/g++.dg/warn/Woverloaded-virt13.C b/gcc/testsuite/g++.dg/warn/Woverloaded-virt13.C
new file mode 100644
index 00000000000..fb421e17dc3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Woverloaded-virt13.C
@@ -0,0 +1,28 @@
+// PR c++/117114 - Reduced version of another bootstrap error
+// Unrelated methods can have the same DECL_VINDEX when the class hierarchy
+// depth is 2 or more.
+// { dg-do compile { target c++11 } }
+// { dg-additional-options -Woverloaded-virtual }
+
+class HIRFullVisitor;
+class HIRTypeVisitor;
+
+struct FullVisitable {
+  virtual void accept_vis (HIRFullVisitor &vis) = 0;
+};
+
+struct Node {
+  virtual ~Node() {}
+};
+
+struct Type : Node, FullVisitable
+{
+  using FullVisitable::accept_vis;
+  virtual void accept_vis (HIRTypeVisitor &vis) = 0;
+};
+
+struct TypePath : Type
+{
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRTypeVisitor &vis) override;
+};
diff --git a/gcc/testsuite/g++.dg/warn/Woverloaded-virt5.C b/gcc/testsuite/g++.dg/warn/Woverloaded-virt5.C
new file mode 100644
index 00000000000..01cd562609a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Woverloaded-virt5.C
@@ -0,0 +1,12 @@
+// PR c++/109918 - Exact PR testcase
+// { dg-do compile }
+// { dg-additional-options -Wall }
+
+struct A {
+  virtual operator int() { return 42; }
+  virtual operator char() = 0;
+};
+
+struct B : public A {
+  operator char() { return 'A'; }
+};
diff --git a/gcc/testsuite/g++.dg/warn/Woverloaded-virt6.C b/gcc/testsuite/g++.dg/warn/Woverloaded-virt6.C
new file mode 100644
index 00000000000..c18049e3a92
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Woverloaded-virt6.C
@@ -0,0 +1,12 @@
+// PR c++/109918 - PR testcase with -Woverloaded-virtual=2
+// { dg-do compile }
+// { dg-additional-options -Woverloaded-virtual=2 }
+
+struct A {
+  virtual operator int() { return 42; }
+  virtual operator char() = 0;
+};
+
+struct B : public A {
+  operator char() { return 'A'; }
+};
diff --git a/gcc/testsuite/g++.dg/warn/Woverloaded-virt7.C b/gcc/testsuite/g++.dg/warn/Woverloaded-virt7.C
new file mode 100644
index 00000000000..5e8227f2793
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Woverloaded-virt7.C
@@ -0,0 +1,31 @@
+// PR c++/109918 - Test different CV-quals and usage of typedefs
+// { dg-do compile }
+// { dg-additional-options -Woverloaded-virtual }
+
+struct A {
+  virtual operator char() { return 'a'; }
+  virtual operator char() const { return 'b'; } // { dg-warning "was hidden" }
+  virtual operator int() { return 42; }
+};
+
+struct B : public A {
+  operator char() { return 'A'; } // { dg-note "by" }
+  operator int() { return 43; }
+};
+
+typedef char MyChar;
+struct C : public A {
+  operator MyChar() { return 'A'; } // { dg-note "by" }
+  operator int() { return 43; }
+};
+
+struct AA {
+  virtual char func(char) { return 'a'; }
+  virtual char func(char) const { return 'b'; } // { dg-warning "was hidden" }
+  virtual int func(int) { return 42; }
+};
+
+struct BB : public AA {
+  char func(char) { return 'A'; } // { dg-note "by" }
+  int func(int) { return 43; } // { dg-note "by" }
+};
diff --git a/gcc/testsuite/g++.dg/warn/Woverloaded-virt8.C b/gcc/testsuite/g++.dg/warn/Woverloaded-virt8.C
new file mode 100644
index 00000000000..51af2dae77c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Woverloaded-virt8.C
@@ -0,0 +1,15 @@
+// Identified when investigating PR c++/109918: no warning was emitted due to
+// an incorrect early return in warn_hidden.
+// { dg-additional-options -Wall }
+
+struct Foo
+{
+  virtual void f(int); // { dg-warning "hidden" }
+  virtual void g() {}
+};
+
+struct Bar : Foo
+{
+  virtual void f(short); // { dg-message "by" }
+  virtual void g() {}
+};
diff --git a/gcc/testsuite/g++.dg/warn/Woverloaded-virt9.C b/gcc/testsuite/g++.dg/warn/Woverloaded-virt9.C
new file mode 100644
index 00000000000..6d315c63a08
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Woverloaded-virt9.C
@@ -0,0 +1,14 @@
+// PR c++/109918: Non virtual overloads in derived classes that don't override
+// anything shouldn't cause warnings, even at -Woverloaded-virtual=2
+// { dg-additional-options -Woverloaded-virtual=2 }
+
+struct Foo
+{
+  virtual void g() {}
+};
+
+struct Bar : Foo
+{
+  virtual void g() {}
+  void g(int) {}
+};
