================
@@ -0,0 +1,212 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "UseAggregateCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::modernize {
+
+/// Check whether \p Ctor is a trivial forwarding constructor: each parameter
+/// is used to initialise the corresponding member (in declaration order) and
+/// the body is empty.
+static bool isTrivialForwardingConstructor(const CXXConstructorDecl *Ctor) {
+  if (!Ctor || !Ctor->hasBody())
+    return false;
+
+  // Body must be an empty compound statement.
+  const auto *Body = dyn_cast<CompoundStmt>(Ctor->getBody());
+  if (!Body || !Body->body_empty())
+    return false;
+
+  const CXXRecordDecl *Record = Ctor->getParent();
+
+  // Collect non-static data members in declaration order.
+  SmallVector<const FieldDecl *, 8> Fields;
+  for (const auto *Field : Record->fields())
+    Fields.push_back(Field);
+
+  // Number of parameters must match number of fields.
+  if (Ctor->getNumParams() != Fields.size())
+    return false;
+
+  // Number of member initializers must match number of fields (no base
+  // class inits, no extra inits).
+  unsigned NumMemberInits = 0;
+  for (const auto *Init : Ctor->inits())
+    if (Init->isMemberInitializer())
+      ++NumMemberInits;
+    else
+      return false; // base class or delegating init -- bail out
+  if (NumMemberInits != Fields.size())
+    return false;
+
+  // Walk initializers and check each one initializes the matching field
+  // from the matching parameter.
+  unsigned FieldIdx = 0;
+  for (const auto *Init : Ctor->inits()) {
+    if (!Init->isMemberInitializer())
+      return false;
+
+    // Must match the field at the current position.
+    if (Init->getMember() != Fields[FieldIdx])
+      return false;
+
+    const Expr *InitExpr = Init->getInit()->IgnoreImplicit();
+
+    // Handle CXXConstructExpr wrapping the parameter (for class types).
+    if (const auto *Construct = dyn_cast<CXXConstructExpr>(InitExpr)) {
+      if (Construct->getNumArgs() != 1)
+        return false;
+      // Must be a copy or move constructor call.
+      const CXXConstructorDecl *InitCtor = Construct->getConstructor();
+      if (!InitCtor->isCopyOrMoveConstructor())
+        return false;
+      InitExpr = Construct->getArg(0)->IgnoreImplicit();
+    }
+
+    // The init expression must be a DeclRefExpr to the corresponding param.
+    const auto *DRE = dyn_cast<DeclRefExpr>(InitExpr);
+    if (!DRE)
+      return false;
+    const auto *PVD = dyn_cast<ParmVarDecl>(DRE->getDecl());
+    if (!PVD || PVD != Ctor->getParamDecl(FieldIdx))
+      return false;
+
+    ++FieldIdx;
+  }
+
+  return true;
+}
+
+/// Check whether the class would be a valid aggregate if all user-declared
+/// constructors were removed.
+static bool canBeAggregate(const CXXRecordDecl *Record,
+                           const LangOptions &LangOpts) {
+  if (!Record || !Record->hasDefinition())
+    return false;
+
+  // Must not have virtual functions.
+  if (Record->isPolymorphic())
+    return false;
+
+  // Must not have private or protected non-static data members.
+  for (const auto *Field : Record->fields())
+    if (Field->getAccess() != AS_public)
+      return false;
+
+  // Must not have virtual, private, or protected base classes.
+  for (const auto &Base : Record->bases()) {
+    if (Base.isVirtual())
+      return false;
+    if (Base.getAccessSpecifier() != AS_public)
+      return false;
+  }
+
+  // C++17 and later allow non-virtual public base classes in aggregates.
+  // Before C++17, aggregates cannot have base classes at all.
+  if (!LangOpts.CPlusPlus17 && Record->getNumBases() > 0)
+    return false;
+
+  return true;
+}
+
+void UseAggregateCheck::registerMatchers(MatchFinder *Finder) {
+  // Match class/struct definitions that have at least one user-provided
+  // constructor.
+  Finder->addMatcher(
+      cxxRecordDecl(
+          isDefinition(), unless(isImplicit()), unless(isLambda()),
+          has(cxxConstructorDecl(isUserProvided(), unless(isDeleted()))))
+          .bind("record"),
+      this);
+}
+
+void UseAggregateCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *Record = Result.Nodes.getNodeAs<CXXRecordDecl>("record");
+  if (!Record)
+    return;
+
+  // Skip records in system headers.
+  if (Record->getLocation().isInvalid() ||
+      Result.SourceManager->isInSystemHeader(Record->getLocation()))
+    return;
+
+  // Skip template specializations to avoid false positives.
+  if (isa<ClassTemplateSpecializationDecl>(Record))
+    return;
+
+  // Skip if no fields (empty structs are already aggregates by default).
+  if (Record->field_empty())
+    return;
+
+  // Check aggregate preconditions (ignoring constructors).
+  if (!canBeAggregate(Record, getLangOpts()))
+    return;
+
+  // Collect all user-declared constructors.
+  SmallVector<const CXXConstructorDecl *, 4> UserCtors;
+  for (const auto *Decl : Record->decls()) {
----------------
zeyi2 wrote:

Can we use `Record->ctors()` directly?

https://github.com/llvm/llvm-project/pull/182061
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to