Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Clang] disallow the use of asterisks preceding constructor and destructor names #122621

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

a-tarasyuk
Copy link
Member

Fixes #121706

@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Jan 11, 2025
@llvmbot
Copy link
Member

llvmbot commented Jan 11, 2025

@llvm/pr-subscribers-clang

Author: Oleksandr T. (a-tarasyuk)

Changes

Fixes #121706


Full diff: https://github.com/llvm/llvm-project/pull/122621.diff

5 Files Affected:

  • (modified) clang/docs/ReleaseNotes.rst (+1)
  • (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+4)
  • (modified) clang/lib/Sema/SemaDeclCXX.cpp (+13)
  • (modified) clang/test/SemaCXX/constructor.cpp (+10)
  • (modified) clang/test/SemaCXX/destructor.cpp (+7)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 197b3692b8a181..9e5bbbf6dc055a 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -764,6 +764,7 @@ Improvements to Clang's diagnostics
       scope.Unlock();
       require(scope); // Warning!  Requires mu1.
     }
+- Clang now disallows the use of asterisks preceding constructor and destructor names (#GH121706).
 
 Improvements to Clang's time-trace
 ----------------------------------
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index d4e897868f1a9a..ec0be4ea8b6ce0 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -2202,6 +2202,8 @@ def err_invalid_qualified_constructor : Error<
   "'%0' qualifier is not allowed on a constructor">;
 def err_ref_qualifier_constructor : Error<
   "ref-qualifier '%select{&&|&}0' is not allowed on a constructor">;
+def err_invalid_constructor_decl : Error<
+  "invalid constructor declaration">;
 
 def err_constructor_return_type : Error<
   "constructor cannot have a return type">;
@@ -2223,6 +2225,8 @@ def err_destructor_not_member : Error<
 def err_destructor_cannot_be : Error<"destructor cannot be declared '%0'">;
 def err_invalid_qualified_destructor : Error<
   "'%0' qualifier is not allowed on a destructor">;
+def err_invalid_destructor_decl : Error<
+  "invalid destructor declaration">;
 def err_ref_qualifier_destructor : Error<
   "ref-qualifier '%select{&&|&}0' is not allowed on a destructor">;
 def err_destructor_return_type : Error<"destructor cannot have a return type">;
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index c4bee44f5ec048..bb6f6e14713d9b 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -10757,6 +10757,17 @@ static void checkMethodTypeQualifiers(Sema &S, Declarator &D, unsigned DiagID) {
   }
 }
 
+static void checkMethodPointerType(Sema &S, Declarator &D, unsigned DiagID) {
+  if (D.getNumTypeObjects() > 0) {
+    DeclaratorChunk &Chunk = D.getTypeObject(D.getNumTypeObjects() - 1);
+    if (Chunk.Kind == DeclaratorChunk::Pointer) {
+      SourceLocation PointerLoc = Chunk.getSourceRange().getBegin();
+      S.Diag(PointerLoc, DiagID) << Chunk.getSourceRange();
+      D.setInvalidType();
+    }
+  }
+}
+
 QualType Sema::CheckConstructorDeclarator(Declarator &D, QualType R,
                                           StorageClass &SC) {
   bool isVirtual = D.getDeclSpec().isVirtualSpecified();
@@ -10792,6 +10803,7 @@ QualType Sema::CheckConstructorDeclarator(Declarator &D, QualType R,
   }
 
   checkMethodTypeQualifiers(*this, D, diag::err_invalid_qualified_constructor);
+  checkMethodPointerType(*this, D, diag::err_invalid_constructor_decl);
 
   // C++0x [class.ctor]p4:
   //   A constructor shall not be declared with a ref-qualifier.
@@ -10958,6 +10970,7 @@ QualType Sema::CheckDestructorDeclarator(Declarator &D, QualType R,
   }
 
   checkMethodTypeQualifiers(*this, D, diag::err_invalid_qualified_destructor);
+  checkMethodPointerType(*this, D, diag::err_invalid_destructor_decl);
 
   // C++0x [class.dtor]p2:
   //   A destructor shall not be declared with a ref-qualifier.
diff --git a/clang/test/SemaCXX/constructor.cpp b/clang/test/SemaCXX/constructor.cpp
index abd7dbe18a0e6a..c6df7be25c1fac 100644
--- a/clang/test/SemaCXX/constructor.cpp
+++ b/clang/test/SemaCXX/constructor.cpp
@@ -96,3 +96,13 @@ namespace PR38286 {
   template<typename> struct C; // expected-note {{non-type declaration found}}
   template<typename T> C<T>::~C() {} // expected-error {{identifier 'C' after '~' in destructor name does not name a type}}
 }
+
+namespace GH121706 {
+struct S {
+  *S();  // expected-error {{invalid constructor declaration}}
+  **S(); // expected-error {{invalid constructor declaration}}
+
+  **S(const S &); // expected-error {{invalid constructor declaration}}
+  *S(S &&);       // expected-error {{invalid constructor declaration}}
+};
+}
diff --git a/clang/test/SemaCXX/destructor.cpp b/clang/test/SemaCXX/destructor.cpp
index dfcd1b033af5a2..f188e95c25d174 100644
--- a/clang/test/SemaCXX/destructor.cpp
+++ b/clang/test/SemaCXX/destructor.cpp
@@ -586,4 +586,11 @@ struct Y : X {} y1{ }; // expected-error {{call to implicitly-deleted default co
                        // expected-note {{default constructor of 'Y' is implicitly deleted because base class 'X' has no destructor}}
 }
 
+namespace GH121706 {
+struct S {
+  *~S();  // expected-error {{invalid destructor declaration}}
+  **~S(); // expected-error {{invalid destructor declaration}}
+};
+}
+
 #endif // BE_THE_HEADER

Comment on lines +10761 to +10770
if (D.getNumTypeObjects() > 0) {
DeclaratorChunk &Chunk = D.getTypeObject(D.getNumTypeObjects() - 1);
if (Chunk.Kind == DeclaratorChunk::Pointer) {
SourceLocation PointerLoc = Chunk.getSourceRange().getBegin();
S.Diag(PointerLoc, DiagID) << Chunk.getSourceRange();
D.setInvalidType();
}
}
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we simplify that by checking that there is only one chunk? (which is a function?)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case, the declarator consists of two chunks: [DeclaratorChunk::Function, DeclaratorChunk::Pointer]

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know, my question is whether there is any case where having more than one chunk would be valid?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean that having more than one chunk should, by default, be considered an error? I thought about that, however, I opted to use the appropriate kind check to ensure cases like this are handled correctly (the helper is used for constructors and destructors).

struct S {
  (~S)();
};

Copy link
Contributor

@cor3ntin cor3ntin Jan 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right, parentheses are valid. Good catch. But everything else is not
https://eel.is/c++draft/class.ctor#general-1

So if any of the chuck is not either Function or Paren it should be an error (if pointer was the only case we missed, maybe an assert instead)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator

@shafik shafik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the fix.

Please add a more detailed summary, the title feels sufficient to describe the problem but you should outline the solution in the summary, at least briefly. e.g. adding checkMethodPointerType which will called during .... to verify ...


namespace GH121706 {
struct S {
*S(); // expected-error {{invalid constructor declaration}}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As pointed out above, we need a lot more tests. The bug report shows several other cases this applies to and they should all be enumerated in the tests.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Clang] Accepts invalid constructor declaration with an asterisk
5 participants