================
@@ -0,0 +1,224 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 "PointerToRefCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::readability {
+
+namespace {
+
+/// Visitor that classifies how a pointer parameter is used in a function body.
+/// It detects:
+///   - Dereferences (operator*, operator->)
+///   - Null checks (if(ptr), ptr != nullptr, assert(ptr), etc.)
+///   - Pointer arithmetic
+///   - Being passed to other functions as a pointer
+///   - Address-of or array subscript usage
+class PointerUsageVisitor : public RecursiveASTVisitor<PointerUsageVisitor> {
+public:
+  PointerUsageVisitor(const ParmVarDecl *Param, ASTContext &Ctx)
+      : Param(Param), Ctx(Ctx) {}
+
+  bool isDereferenced() const { return Dereferenced; }
+  bool isNullChecked() const { return NullChecked; }
+  bool isUsedAsPointer() const { return UsedAsPointer; }
+
+  bool VisitUnaryOperator(const UnaryOperator *UO) {
+    const Expr *SubExpr = UO->getSubExpr();
+    if (UO->getOpcode() == UO_Deref && refersToParam(SubExpr))
+      Dereferenced = true;
+    if (UO->getOpcode() == UO_AddrOf && refersToParam(SubExpr))
+      UsedAsPointer = true;
+    return true;
+  }
+
+  bool VisitMemberExpr(const MemberExpr *ME) {
+    if (ME->isArrow() && refersToParam(ME->getBase()))
+      Dereferenced = true;
+    return true;
+  }
+
+  // Detect null checks: if(ptr), ptr == nullptr, !ptr, etc.
+  bool VisitImplicitCastExpr(const ImplicitCastExpr *ICE) {
+    if (ICE->getCastKind() == CK_PointerToBoolean &&
+        refersToParam(ICE->getSubExpr()))
+      NullChecked = true;
+    return true;
+  }
+
+  bool VisitBinaryOperator(const BinaryOperator *BO) {
+    const Expr *LHS = BO->getLHS()->IgnoreImplicit();
+    const Expr *RHS = BO->getRHS()->IgnoreImplicit();
+
+    // ptr == nullptr, nullptr == ptr, ptr != nullptr, etc.
+    if (BO->isEqualityOp()) {
+      const bool LHSIsParam = refersToParam(LHS);
+      const bool RHSIsParam = refersToParam(RHS);
+      if ((LHSIsParam && RHS->isNullPointerConstant(
+                             Ctx, Expr::NPC_ValueDependentIsNotNull)) ||
+          (RHSIsParam &&
+           LHS->isNullPointerConstant(Ctx, Expr::NPC_ValueDependentIsNotNull)))
+        NullChecked = true;
+      // Comparing pointer to another non-null pointer (e.g. p == q).
+      else if (LHSIsParam || RHSIsParam)
+        UsedAsPointer = true;
+    }
+
+    // Pointer comparison (relational).
+    if (BO->isRelationalOp() && (refersToParam(LHS) || refersToParam(RHS)))
+      UsedAsPointer = true;
+
+    // Pointer arithmetic: ptr + n, ptr - n.
+    if ((BO->getOpcode() == BO_Add || BO->getOpcode() == BO_Sub) &&
+        (refersToParam(LHS) || refersToParam(RHS)))
+      UsedAsPointer = true;
+
+    // Reassignment: ptr = something.
+    if (BO->getOpcode() == BO_Assign && refersToParam(LHS))
+      UsedAsPointer = true;
+
+    return true;
+  }
+
+  // Detect being passed to a function expecting a pointer.
+  bool VisitCallExpr(const CallExpr *CE) {
+    for (unsigned I = 0; I < CE->getNumArgs(); ++I)
+      if (refersToParam(CE->getArg(I)->IgnoreImplicit()))
+        UsedAsPointer = true;
+    return true;
+  }
+
+  // Detect array subscript: ptr[i]
+  bool VisitArraySubscriptExpr(const ArraySubscriptExpr *ASE) {
+    if (refersToParam(ASE->getBase()->IgnoreImplicit()))
+      UsedAsPointer = true;
+    return true;
+  }
+
+  // Detect delete expression: delete ptr.
+  bool VisitCXXDeleteExpr(const CXXDeleteExpr *DE) {
+    if (refersToParam(DE->getArgument()))
+      UsedAsPointer = true;
+    return true;
+  }
+
+  // Detect return of pointer value.
+  bool VisitReturnStmt(const ReturnStmt *RS) {
+    if (RS->getRetValue() && refersToParam(RS->getRetValue()))
+      UsedAsPointer = true;
+    return true;
+  }
+
+  // Detect pointer stored to a variable: int *q = ptr;
+  bool VisitVarDecl(const VarDecl *VD) {
+    if (VD != Param && VD->hasInit() && refersToParam(VD->getInit()))
+      UsedAsPointer = true;
+    return true;
+  }
+
+  // Detect pointer passed to constructor: SomeClass obj(ptr);
+  bool VisitCXXConstructExpr(const CXXConstructExpr *CE) {
+    for (unsigned I = 0; I < CE->getNumArgs(); ++I)
+      if (refersToParam(CE->getArg(I)))
+        UsedAsPointer = true;
+    return true;
+  }
+
+  // Detect pointer captured by lambda.
+  bool TraverseLambdaExpr(LambdaExpr *LE) {
+    for (const LambdaCapture &Capture : LE->captures())
+      if (Capture.capturesVariable() && Capture.getCapturedVar() == Param)
+        UsedAsPointer = true;
+    // Don't descend into the lambda body -- it's a different scope.
+    return true;
+  }
+
+  // Skip unevaluated contexts like sizeof.
+  bool TraverseUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *E) {
+    // Do not descend into sizeof/alignof -- the pointer is not actually used.
+    return true;
+  }
+
+private:
+  bool refersToParam(const Expr *E) const {
+    if (!E)
+      return false;
+    const auto *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts());
+    return DRE && DRE->getDecl() == Param;
+  }
+
+  const ParmVarDecl *Param;
+  ASTContext &Ctx;
+  bool Dereferenced = false;
+  bool NullChecked = false;
+  bool UsedAsPointer = false;
+};
+
+} // namespace
+
+AST_MATCHER(CXXMethodDecl, isOverloadedOperator) {
+  return Node.isOverloadedOperator();
+}
+
+void PointerToRefCheck::registerMatchers(MatchFinder *Finder) {
+  // Match pointer parameters in non-trivial function definitions,
+  // excluding void pointers and function pointers.
+  Finder->addMatcher(
+      parmVarDecl(
+          hasType(
+              pointerType(pointee(unless(voidType()), 
unless(functionType())))),
+          hasParent(functionDecl(isDefinition(), unless(isImplicit()),
+                                 unless(isDeleted()), unless(isVariadic()),
+                                 unless(isMain()),
+                                 unless(cxxMethodDecl(isVirtual())),
+                                 unless(cxxMethodDecl(isOverloadedOperator())))
+                        .bind("func")))
+          .bind("param"),
+      this);
+}
+
+void PointerToRefCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *Func = Result.Nodes.getNodeAs<FunctionDecl>("func");
+  const auto *Param = Result.Nodes.getNodeAs<ParmVarDecl>("param");
+  if (!Func || !Param || !Func->hasBody())
----------------
zwuis wrote:

Use `assert`.

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

Reply via email to