https://github.com/DKLoehr updated 
https://github.com/llvm/llvm-project/pull/131188

>From fbd474fb5ae3adeaf1644a4d44e916e4d7c66395 Mon Sep 17 00:00:00 2001
From: Devon Loehr <dlo...@google.com>
Date: Thu, 13 Mar 2025 17:34:27 +0000
Subject: [PATCH 1/4] Initial warning commit

---
 clang/include/clang/Basic/DiagnosticGroups.td |  2 ++
 .../clang/Basic/DiagnosticSemaKinds.td        |  3 +++
 clang/lib/Sema/SemaDeclCXX.cpp                |  5 ++++
 .../SemaCXX/unnecessary-virtual-specifier.cpp | 27 +++++++++++++++++++
 4 files changed, 37 insertions(+)
 create mode 100644 clang/test/SemaCXX/unnecessary-virtual-specifier.cpp

diff --git a/clang/include/clang/Basic/DiagnosticGroups.td 
b/clang/include/clang/Basic/DiagnosticGroups.td
index fac80fb4009aa..65593ddcb8608 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -372,6 +372,8 @@ def CXX11WarnInconsistentOverrideMethod :
 def CXX11WarnSuggestOverrideDestructor : 
DiagGroup<"suggest-destructor-override">;
 def CXX11WarnSuggestOverride : DiagGroup<"suggest-override">;
 
+def WarnUnnecessaryVirtualSpecifier : 
DiagGroup<"unnecessary-virtual-specifier">;
+
 // Original name of this warning in Clang
 def : DiagGroup<"c++0x-narrowing", [CXX11Narrowing]>;
 
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 8e6e6e892cdd7..a87cf7e674b4c 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -2706,6 +2706,9 @@ def warn_final_dtor_non_final_class : Warning<
   InGroup<FinalDtorNonFinalClass>;
 def note_final_dtor_non_final_class_silence : Note<
   "mark %0 as '%select{final|sealed}1' to silence this warning">;
+def warn_unnecessary_virtual_specifier : Warning<
+  "virtual method %0 is inside a 'final' class and can never be overridden">,
+  InGroup<WarnUnnecessaryVirtualSpecifier>;
 
 // C++11 attributes
 def err_repeat_attribute : Error<"%0 attribute cannot be repeated">;
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index dd779ee377309..1b2e494956d4b 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -7193,10 +7193,15 @@ void Sema::CheckCompletedCXXClass(Scope *S, 
CXXRecordDecl *Record) {
         // class without overriding any.
         if (!M->isStatic())
           DiagnoseHiddenVirtualMethods(M);
+
         if (M->hasAttr<OverrideAttr>())
           HasMethodWithOverrideControl = true;
         else if (M->size_overridden_methods() > 0)
           HasOverridingMethodWithoutOverrideControl = true;
+
+        // See if a method is marked as virtual inside of a final class.
+        if (M->isVirtualAsWritten() && Record->isEffectivelyFinal())
+          Diag(M->getLocation(), diag::warn_unnecessary_virtual_specifier) << 
M;
       }
 
       if (!isa<CXXDestructorDecl>(M))
diff --git a/clang/test/SemaCXX/unnecessary-virtual-specifier.cpp 
b/clang/test/SemaCXX/unnecessary-virtual-specifier.cpp
new file mode 100644
index 0000000000000..eb8397d9ade45
--- /dev/null
+++ b/clang/test/SemaCXX/unnecessary-virtual-specifier.cpp
@@ -0,0 +1,27 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -Wunnecessary-virtual-specifier 
-Wsuggest-override %s
+
+struct Foo final {
+  Foo() = default;
+       virtual ~Foo() = default;                      // expected-warning 
{{virtual method}}
+       virtual Foo& operator=(Foo& other) = default;  // expected-warning 
{{virtual method}}
+       virtual Foo& operator=(Foo&& other) = default; // expected-warning 
{{virtual method}}
+       void f();
+  virtual void f(int);                           // expected-warning {{virtual 
method}}
+  int g(int x) { return x; };
+  virtual int g(bool);                           // expected-warning {{virtual 
method}}
+  static int s();
+};
+
+struct BarBase {
+  virtual ~BarBase() = delete;
+       virtual void virt() {}
+       virtual int virt(int);
+  int nonvirt();
+};
+
+struct Bar final : BarBase {
+  ~Bar() override = delete;
+       void virt() override {};
+       virtual int virt(int) override;                // expected-warning 
{{virtual method}}
+  int nonvirt();
+};

>From 4a16ef81a4882785708a4973f78586119235e8f5 Mon Sep 17 00:00:00 2001
From: Devon Loehr <dlo...@google.com>
Date: Thu, 13 Mar 2025 17:59:39 +0000
Subject: [PATCH 2/4] Add documentation

---
 clang/docs/ReleaseNotes.rst                   |  3 +++
 clang/include/clang/Basic/DiagnosticGroups.td | 11 ++++++++++-
 2 files changed, 13 insertions(+), 1 deletion(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 8989124611e66..15858631c17b7 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -247,6 +247,9 @@ Improvements to Clang's diagnostics
 - The ``-Wsign-compare`` warning now treats expressions with bitwise not(~) 
and minus(-) as signed integers 
   except for the case where the operand is an unsigned integer
   and throws warning if they are compared with unsigned integers (##18878).
+- The ``-Wunnecessary-virtual-specifier`` warning has been added to warn about
+  methods which are marked as virtual inside a ``final`` class, and hence can
+  never be overridden.
 
 - Improve the diagnostics for chained comparisons to report actual expressions 
and operators (#GH129069).
 
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td 
b/clang/include/clang/Basic/DiagnosticGroups.td
index 65593ddcb8608..e785e84205192 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -372,7 +372,16 @@ def CXX11WarnInconsistentOverrideMethod :
 def CXX11WarnSuggestOverrideDestructor : 
DiagGroup<"suggest-destructor-override">;
 def CXX11WarnSuggestOverride : DiagGroup<"suggest-override">;
 
-def WarnUnnecessaryVirtualSpecifier : 
DiagGroup<"unnecessary-virtual-specifier">;
+def WarnUnnecessaryVirtualSpecifier : 
DiagGroup<"unnecessary-virtual-specifier"> {
+  code Documentation = [{
+Warns when a ``final`` class contains a virtual method (including virtual
+destructors). Since ``final`` classes cannot be subclassed, their methods
+cannot be overridden, and hence the ``virtual`` specifier is useless.
+
+The warning also detects virtual methods in classes whose destructor is
+``final``, for the same reason.
+  }];
+}
 
 // Original name of this warning in Clang
 def : DiagGroup<"c++0x-narrowing", [CXX11Narrowing]>;

>From 11f33b66533f58a5180f58581c21067b9a422b62 Mon Sep 17 00:00:00 2001
From: Devon Loehr <dlo...@google.com>
Date: Thu, 13 Mar 2025 18:18:15 +0000
Subject: [PATCH 3/4] Make DefaultIgnore

---
 clang/include/clang/Basic/DiagnosticSemaKinds.td     | 2 +-
 clang/test/SemaCXX/unnecessary-virtual-specifier.cpp | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index a87cf7e674b4c..ed31c2d225073 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -2708,7 +2708,7 @@ def note_final_dtor_non_final_class_silence : Note<
   "mark %0 as '%select{final|sealed}1' to silence this warning">;
 def warn_unnecessary_virtual_specifier : Warning<
   "virtual method %0 is inside a 'final' class and can never be overridden">,
-  InGroup<WarnUnnecessaryVirtualSpecifier>;
+  InGroup<WarnUnnecessaryVirtualSpecifier>, DefaultIgnore;
 
 // C++11 attributes
 def err_repeat_attribute : Error<"%0 attribute cannot be repeated">;
diff --git a/clang/test/SemaCXX/unnecessary-virtual-specifier.cpp 
b/clang/test/SemaCXX/unnecessary-virtual-specifier.cpp
index eb8397d9ade45..00f696bfa0d4f 100644
--- a/clang/test/SemaCXX/unnecessary-virtual-specifier.cpp
+++ b/clang/test/SemaCXX/unnecessary-virtual-specifier.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fsyntax-only -verify -Wunnecessary-virtual-specifier 
-Wsuggest-override %s
+// RUN: %clang_cc1 -fsyntax-only -verify -Wunnecessary-virtual-specifier %s
 
 struct Foo final {
   Foo() = default;

>From eb03ecf974d475021233cdf63e50dad804132bff Mon Sep 17 00:00:00 2001
From: Devon Loehr <dlo...@google.com>
Date: Fri, 14 Mar 2025 14:24:13 +0000
Subject: [PATCH 4/4] Don't warn on virtual...override.

---
 clang/include/clang/Basic/DiagnosticGroups.td  |  2 ++
 clang/lib/Sema/SemaDeclCXX.cpp                 | 18 +++++++++++-------
 .../SemaCXX/unnecessary-virtual-specifier.cpp  | 17 +++++++++--------
 3 files changed, 22 insertions(+), 15 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticGroups.td 
b/clang/include/clang/Basic/DiagnosticGroups.td
index e785e84205192..e54f921741269 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -380,6 +380,8 @@ cannot be overridden, and hence the ``virtual`` specifier 
is useless.
 
 The warning also detects virtual methods in classes whose destructor is
 ``final``, for the same reason.
+
+The warning does not fire on virtual methods which are also marked 
``override``.
   }];
 }
 
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 1b2e494956d4b..381b81b5b832a 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -7194,14 +7194,18 @@ void Sema::CheckCompletedCXXClass(Scope *S, 
CXXRecordDecl *Record) {
         if (!M->isStatic())
           DiagnoseHiddenVirtualMethods(M);
 
-        if (M->hasAttr<OverrideAttr>())
+        if (M->hasAttr<OverrideAttr>()) {
           HasMethodWithOverrideControl = true;
-        else if (M->size_overridden_methods() > 0)
-          HasOverridingMethodWithoutOverrideControl = true;
-
-        // See if a method is marked as virtual inside of a final class.
-        if (M->isVirtualAsWritten() && Record->isEffectivelyFinal())
-          Diag(M->getLocation(), diag::warn_unnecessary_virtual_specifier) << 
M;
+        } else {
+          if (M->size_overridden_methods() > 0)
+            HasOverridingMethodWithoutOverrideControl = true;
+
+          // Warn on virtual methods in final classes, unless they're also
+          // marked `override`.
+          if (M->isVirtualAsWritten() && Record->isEffectivelyFinal())
+            Diag(M->getLocation(), diag::warn_unnecessary_virtual_specifier)
+                << M;
+        }
       }
 
       if (!isa<CXXDestructorDecl>(M))
diff --git a/clang/test/SemaCXX/unnecessary-virtual-specifier.cpp 
b/clang/test/SemaCXX/unnecessary-virtual-specifier.cpp
index 00f696bfa0d4f..b1fa6145e34e1 100644
--- a/clang/test/SemaCXX/unnecessary-virtual-specifier.cpp
+++ b/clang/test/SemaCXX/unnecessary-virtual-specifier.cpp
@@ -2,10 +2,10 @@
 
 struct Foo final {
   Foo() = default;
-       virtual ~Foo() = default;                      // expected-warning 
{{virtual method}}
-       virtual Foo& operator=(Foo& other) = default;  // expected-warning 
{{virtual method}}
-       virtual Foo& operator=(Foo&& other) = default; // expected-warning 
{{virtual method}}
-       void f();
+  virtual ~Foo() = default;                      // expected-warning {{virtual 
method}}
+  virtual Foo& operator=(Foo& other) = default;  // expected-warning {{virtual 
method}}
+  virtual Foo& operator=(Foo&& other) = default; // expected-warning {{virtual 
method}}
+  void f();
   virtual void f(int);                           // expected-warning {{virtual 
method}}
   int g(int x) { return x; };
   virtual int g(bool);                           // expected-warning {{virtual 
method}}
@@ -14,14 +14,15 @@ struct Foo final {
 
 struct BarBase {
   virtual ~BarBase() = delete;
-       virtual void virt() {}
-       virtual int virt(int);
+  virtual void virt() {}
+  virtual int virt(int);
   int nonvirt();
 };
 
 struct Bar final : BarBase {
   ~Bar() override = delete;
-       void virt() override {};
-       virtual int virt(int) override;                // expected-warning 
{{virtual method}}
+  void virt() override {};
+  // `virtual ... override;` is a common pattern, so don't warn
+  virtual int virt(int) override;
   int nonvirt();
 };

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to