@@ -428,6 +428,31 @@ class FactGeneratorVisitor : public ConstStmtVisitor<FactGeneratorVisitor> {
428428 addAssignOriginFact (*VD, *InitExpr);
429429 }
430430
431+ void VisitCXXConstructExpr (const CXXConstructExpr *CCE) {
432+ if (!isGslPointerType (CCE->getType ()))
433+ return ;
434+
435+ if (CCE->getNumArgs () > 0 && hasOrigin (CCE->getArg (0 )->getType ()))
436+ // This is a propagation.
437+ addAssignOriginFact (*CCE, *CCE->getArg (0 ));
438+ else
439+ // This could be a new borrow.
440+ checkForBorrows (CCE, CCE->getConstructor (),
441+ {CCE->getArgs (), CCE->getNumArgs ()});
442+ }
443+
444+ void VisitCXXMemberCallExpr (const CXXMemberCallExpr *MCE) {
445+ if (!isGslPointerType (MCE->getImplicitObjectArgument ()->getType ()))
446+ return ;
447+ // Specifically for conversion operators, like `std::string_view p = a;`
448+ if (isa<CXXConversionDecl>(MCE->getCalleeDecl ())) {
449+ // The argument is the implicit object itself.
450+ checkForBorrows (MCE, MCE->getMethodDecl (),
451+ {MCE->getImplicitObjectArgument ()});
452+ }
453+ // Note: A more general VisitCallExpr could also be used here.
454+ }
455+
431456 void VisitCXXNullPtrLiteralExpr (const CXXNullPtrLiteralExpr *N) {
432457 // / TODO: Handle nullptr expr as a special 'null' loan. Uninitialized
433458 // / pointers can use the same type of loan.
@@ -479,29 +504,39 @@ class FactGeneratorVisitor : public ConstStmtVisitor<FactGeneratorVisitor> {
479504 }
480505
481506 void VisitBinaryOperator (const BinaryOperator *BO) {
482- if (BO->isAssignmentOp ()) {
483- const Expr *LHSExpr = BO->getLHS ();
484- const Expr *RHSExpr = BO->getRHS ();
485-
486- // We are interested in assignments like `ptr1 = ptr2` or `ptr = &var`
487- // LHS must be a pointer/reference type that can be an origin.
488- // RHS must also represent an origin (either another pointer/ref or an
489- // address-of).
490- if (const auto *DRE_LHS = dyn_cast<DeclRefExpr>(LHSExpr))
491- if (const auto *VD_LHS =
492- dyn_cast<ValueDecl>(DRE_LHS->getDecl ()->getCanonicalDecl ());
493- VD_LHS && hasOrigin (VD_LHS->getType ()))
494- addAssignOriginFact (*VD_LHS, *RHSExpr);
495- }
507+ if (BO->isAssignmentOp ())
508+ handleAssignment (BO->getLHS (), BO->getRHS ());
509+ }
510+
511+ void VisitCXXOperatorCallExpr (const CXXOperatorCallExpr *OCE) {
512+ if (OCE->isAssignmentOp () && OCE->getNumArgs () == 2 )
513+ handleAssignment (OCE->getArg (0 ), OCE->getArg (1 ));
496514 }
497515
498516 void VisitCXXFunctionalCastExpr (const CXXFunctionalCastExpr *FCE) {
499517 // Check if this is a test point marker. If so, we are done with this
500518 // expression.
501519 if (VisitTestPoint (FCE))
502520 return ;
503- // Visit as normal otherwise.
504- Base::VisitCXXFunctionalCastExpr (FCE);
521+ if (isGslPointerType (FCE->getType ()))
522+ addAssignOriginFact (*FCE, *FCE->getSubExpr ());
523+ }
524+
525+ void VisitInitListExpr (const InitListExpr *ILE) {
526+ if (!hasOrigin (ILE->getType ()))
527+ return ;
528+ // For list initialization with a single element, like `View{...}`, the
529+ // origin of the list itself is the origin of its single element.
530+ if (ILE->getNumInits () == 1 )
531+ addAssignOriginFact (*ILE, *ILE->getInit (0 ));
532+ }
533+
534+ void VisitMaterializeTemporaryExpr (const MaterializeTemporaryExpr *MTE) {
535+ if (!hasOrigin (MTE->getType ()))
536+ return ;
537+ // A temporary object's origin is the same as the origin of the
538+ // expression that initializes it.
539+ addAssignOriginFact (*MTE, *MTE->getSubExpr ());
505540 }
506541
507542 void handleDestructor (const CFGAutomaticObjDtor &DtorOpt) {
@@ -527,8 +562,88 @@ class FactGeneratorVisitor : public ConstStmtVisitor<FactGeneratorVisitor> {
527562 }
528563
529564private:
565+ static bool isGslPointerType (QualType QT) {
566+ if (const auto *RD = QT->getAsCXXRecordDecl ()) {
567+ // We need to check the template definition for specializations.
568+ if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(RD))
569+ return CTSD->getSpecializedTemplate ()
570+ ->getTemplatedDecl ()
571+ ->hasAttr <PointerAttr>();
572+ return RD->hasAttr <PointerAttr>();
573+ }
574+ return false ;
575+ }
576+
530577 // Check if a type has an origin.
531- bool hasOrigin (QualType QT) { return QT->isPointerOrReferenceType (); }
578+ static bool hasOrigin (QualType QT) {
579+ if (QT->isFunctionPointerType ())
580+ return false ;
581+ return QT->isPointerOrReferenceType () || isGslPointerType (QT);
582+ }
583+
584+ // / Checks if a call-like expression creates a borrow by passing a local
585+ // / value to a reference parameter, creating an IssueFact if it does.
586+ void checkForBorrows (const Expr *Call, const FunctionDecl *FD,
587+ ArrayRef<const Expr *> Args) {
588+ if (!FD)
589+ return ;
590+
591+ for (unsigned I = 0 ; I < Args.size (); ++I) {
592+ if (I >= FD->getNumParams ())
593+ break ;
594+
595+ const ParmVarDecl *Param = FD->getParamDecl (I);
596+ const Expr *Arg = Args[I];
597+
598+ // This is the core condition for a new borrow: a value type (no origin)
599+ // is passed to a reference parameter.
600+ if (Param->getType ()->isReferenceType () && !hasOrigin (Arg->getType ())) {
601+ if (const Loan *L = createLoanFrom (Arg, Call)) {
602+ OriginID OID = FactMgr.getOriginMgr ().getOrCreate (*Call);
603+ CurrentBlockFacts.push_back (
604+ FactMgr.createFact <IssueFact>(L->ID , OID));
605+ // For view creation, we assume the first borrow is the significant
606+ // one.
607+ return ;
608+ }
609+ }
610+ }
611+ }
612+
613+ // / Attempts to create a loan by analyzing the source expression of a borrow.
614+ // / This method is the single point for creating loans, allowing for future
615+ // / expansion to handle temporaries, field members, etc.
616+ // / \param SourceExpr The expression representing the object being borrowed
617+ // / from.
618+ // / \param IssueExpr The expression that triggers the borrow (e.g., a
619+ // / constructor call).
620+ // / \return The new Loan on success, nullptr on failure.
621+ const Loan *createLoanFrom (const Expr *SourceExpr, const Expr *IssueExpr) {
622+ // For now, we only handle direct borrows from local variables.
623+ // In the future, this can be extended to handle MaterializeTemporaryExpr,
624+ // etc.
625+ if (const auto *DRE =
626+ dyn_cast<DeclRefExpr>(SourceExpr->IgnoreParenImpCasts ())) {
627+ if (const auto *VD = dyn_cast<ValueDecl>(DRE->getDecl ())) {
628+ AccessPath Path (VD);
629+ return &FactMgr.getLoanMgr ().addLoan (Path, IssueExpr);
630+ }
631+ }
632+ return nullptr ;
633+ }
634+
635+ void handleAssignment (const Expr *LHSExpr, const Expr *RHSExpr) {
636+ // Find the underlying variable declaration for the left-hand side.
637+ if (const auto *DRE_LHS =
638+ dyn_cast<DeclRefExpr>(LHSExpr->IgnoreParenImpCasts ()))
639+ if (const auto *VD_LHS = dyn_cast<ValueDecl>(DRE_LHS->getDecl ()))
640+ if (hasOrigin (VD_LHS->getType ()))
641+ // We are interested in assignments like `ptr1 = ptr2` or `ptr = &var`
642+ // LHS must be a pointer/reference type that can be an origin.
643+ // RHS must also represent an origin (either another pointer/ref or an
644+ // address-of).
645+ addAssignOriginFact (*VD_LHS, *RHSExpr);
646+ }
532647
533648 template <typename Destination, typename Source>
534649 void addAssignOriginFact (const Destination &D, const Source &S) {
@@ -578,10 +693,13 @@ class FactGenerator : public RecursiveASTVisitor<FactGenerator> {
578693 for (const CFGBlock *Block : *AC.getAnalysis <PostOrderCFGView>()) {
579694 FactGeneratorBlockRAII BlockGenerator (FG, Block);
580695 for (const CFGElement &Element : *Block) {
581- if (std::optional<CFGStmt> CS = Element.getAs <CFGStmt>())
696+ if (std::optional<CFGStmt> CS = Element.getAs <CFGStmt>()) {
697+ DEBUG_WITH_TYPE (" PrintCFG" , llvm::dbgs () << " ================== \n " );
698+ DEBUG_WITH_TYPE (" PrintCFG" , CS->dump ());
699+ DEBUG_WITH_TYPE (" PrintCFG" , CS->getStmt ()->dumpColor ());
582700 TraverseStmt (const_cast <Stmt *>(CS->getStmt ()));
583- else if (std::optional<CFGAutomaticObjDtor> DtorOpt =
584- Element.getAs <CFGAutomaticObjDtor>())
701+ } else if (std::optional<CFGAutomaticObjDtor> DtorOpt =
702+ Element.getAs <CFGAutomaticObjDtor>())
585703 FG.handleDestructor (*DtorOpt);
586704 }
587705 }
0 commit comments