================
@@ -0,0 +1,199 @@
+//===----------------------------------------------------------------------===//
+//
+// 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:
+  explicit PointerUsageVisitor(const ParmVarDecl *Param) : Param(Param) {}
+
+  bool isDereferenced() const { return Dereferenced; }
+  bool isNullChecked() const { return NullChecked; }
+  bool isUsedAsPointer() const { return UsedAsPointer; }
+
+  bool VisitUnaryOperator(const UnaryOperator *UO) {
+    if (UO->getOpcode() == UO_Deref && refersToParam(UO->getSubExpr()))
+      Dereferenced = 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()) {
+      if ((refersToParam(LHS) && isNullExpr(RHS)) ||
+          (refersToParam(RHS) && isNullExpr(LHS)))
+        NullChecked = 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 address-taken or assigned to another pointer.
+  bool VisitDeclRefExpr(const DeclRefExpr *DRE) {
+    // We handle specific patterns above; just track raw usage for
+    // cases we might miss (e.g. storing to another variable).
+    return true;
+  }
+
+private:
+  bool refersToParam(const Expr *E) const {
+    if (!E)
+      return false;
+    const auto *DRE = dyn_cast<DeclRefExpr>(E->IgnoreImplicit());
+    return DRE && DRE->getDecl() == Param;
+  }
+
+  static bool isNullExpr(const Expr *E) {
+    if (!E)
+      return false;
+    E = E->IgnoreImplicit();
+    if (isa<GNUNullExpr>(E))
+      return true;
+    if (const auto *IL = dyn_cast<IntegerLiteral>(E))
+      return IL->getValue() == 0;
+    if (isa<CXXNullPtrLiteralExpr>(E))
+      return true;
+    return false;
+  }
+
+  const ParmVarDecl *Param;
+  bool Dereferenced = false;
+  bool NullChecked = false;
+  bool UsedAsPointer = false;
+};
+
+} // namespace
+
+void PointerToRefCheck::registerMatchers(MatchFinder *Finder) {
+  // Match function definitions with at least one pointer parameter.
+  // Match each pointer parameter individually.
+  Finder->addMatcher(
+      parmVarDecl(hasType(pointerType()),
+                  hasAncestor(functionDecl(isDefinition(), 
unless(isImplicit()),
----------------
zwuis wrote:

One function might has more than one parameters to be diagnosed. Just changing 
`hasAncestor` to `hasParent` is ok to me.

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