[clang] [LifetimeSafety] Trace assignment history for use-after-scope errors (PR #188467)
Gábor Horváth via cfe-commits
cfe-commits at lists.llvm.org
Fri Apr 10 03:03:36 PDT 2026
================
@@ -0,0 +1,365 @@
+//===- AssignmentQuery.cpp - C++ Lifetime Safety Checker --------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the LifetimeChecker, which detects use-after-free
+// errors by checking if live origins hold loans that have expired.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Analysis/Analyses/LifetimeSafety/AssignmentQuery.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/ParentMap.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/Origins.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include <cstddef>
+
+namespace {
+
+using namespace clang;
+using namespace clang::lifetimes;
+using namespace clang::lifetimes::internal;
+
+std::optional<const Expr *> GetPureSrcExpr(const Expr *TargetExpr) {
+ if (!TargetExpr)
+ return std::nullopt;
+ const Expr *SExpr = TargetExpr->IgnoreParenCasts();
+ if (!SExpr)
+ return std::nullopt;
+
+ if (llvm::isa<DeclRefExpr, CXXTemporaryObjectExpr, ConditionalOperator,
+ CXXConstructExpr>(SExpr) &&
+ !SExpr->getExprLoc().isInvalid())
+ return SExpr;
+
+ if (const auto *SCExpr = llvm::dyn_cast<CallExpr>(SExpr);
+ SCExpr && !SCExpr->getExprLoc().isInvalid() &&
+ !SCExpr->getCallee()->IgnoreParenCasts()->getExprLoc().isInvalid())
+ return SCExpr;
+
+ if (const auto *SMExpr = llvm::dyn_cast<MemberExpr>(SExpr))
+ return GetPureSrcExpr(SMExpr->getBase());
+ if (const auto *SCExpr = llvm::dyn_cast<CXXMemberCallExpr>(SExpr))
+ return GetPureSrcExpr(SCExpr->getCallee());
+ if (const auto *SUOExpr = llvm::dyn_cast<UnaryOperator>(SExpr))
+ return GetPureSrcExpr(SUOExpr->getSubExpr());
+ if (const auto *SCBExpr = llvm::dyn_cast<CXXBindTemporaryExpr>(SExpr))
+ return GetPureSrcExpr(SCBExpr->getSubExpr());
+
+ return std::nullopt;
+}
+
+/// Specifically handles assignments involving a FieldDecl.
+///
+/// Since we currently only store the FieldDecl without its corresponding
+/// LHS expression, this function attempts to recover or resolve the LHS
+/// context by analyzing the RHS.
+const MemberExpr *getFieldFromAssignmentExpr(const Expr *RHS,
+ const ParentMap &CurrParentMap) {
+
+ const Stmt *CurrStmt = CurrParentMap.getParent(RHS);
+ if (!CurrStmt)
+ return nullptr;
+ if (const auto *BinaryOp = llvm::dyn_cast<BinaryOperator>(CurrStmt))
+ return llvm::dyn_cast<MemberExpr>(BinaryOp->getLHS());
+ if (const auto *CXXOp = llvm::dyn_cast<CXXOperatorCallExpr>(CurrStmt);
+ CXXOp && CXXOp->getOperator() == OO_Equal && CXXOp->getNumArgs() == 2)
+ return llvm::dyn_cast<MemberExpr>(CXXOp->getArg(0));
+ return nullptr;
+}
+
+const DeclRefExpr *getLHSExpr(const UseFact *UF, const OriginID OID) {
+ for (const OriginList *Cur = UF->getUsedOrigins(); Cur;
+ Cur = Cur->peelOuterOrigin()) {
+ if (Cur->getOuterOriginID() != OID || !UF->isWritten())
+ continue;
+ std::optional<const Expr *> UExpr = GetPureSrcExpr(UF->getUseExpr());
+ if (UExpr) {
+ if (const auto *UDExpr = llvm::dyn_cast<DeclRefExpr>(UExpr.value())) {
+ return UDExpr;
+ }
+ }
+ }
+ return nullptr;
+}
+
+std::optional<OriginDestExpr>
+getLHSDeclOrExpr(const AssignmentQueryContext &Context,
+ const OriginFlowFact *OFF) {
+ const Origin TargetOrigin =
+ Context.FactMgr.getOriginMgr().getOrigin(OFF->getDestOriginID());
+ if (const ValueDecl *DVecl = TargetOrigin.getDecl();
----------------
Xazax-hun wrote:
What is `DVecl`? Could we have a more descriptive name?
https://github.com/llvm/llvm-project/pull/188467
More information about the cfe-commits
mailing list