@@ -433,6 +433,31 @@ class FactGeneratorVisitor : public ConstStmtVisitor<FactGeneratorVisitor> {
433
433
434
434
void VisitDeclRefExpr (const DeclRefExpr *DRE) { handleUse (DRE); }
435
435
436
+ void VisitCXXConstructExpr (const CXXConstructExpr *CCE) {
437
+ if (!isGslPointerType (CCE->getType ()))
438
+ return ;
439
+
440
+ if (CCE->getNumArgs () > 0 && hasOrigin (CCE->getArg (0 )->getType ()))
441
+ // This is a propagation.
442
+ addAssignOriginFact (*CCE, *CCE->getArg (0 ));
443
+ else
444
+ // This could be a new borrow.
445
+ checkForBorrows (CCE, CCE->getConstructor (),
446
+ {CCE->getArgs (), CCE->getNumArgs ()});
447
+ }
448
+
449
+ void VisitCXXMemberCallExpr (const CXXMemberCallExpr *MCE) {
450
+ if (!isGslPointerType (MCE->getImplicitObjectArgument ()->getType ()))
451
+ return ;
452
+ // Specifically for conversion operators, like `std::string_view p = a;`
453
+ if (isa<CXXConversionDecl>(MCE->getCalleeDecl ())) {
454
+ // The argument is the implicit object itself.
455
+ checkForBorrows (MCE, MCE->getMethodDecl (),
456
+ {MCE->getImplicitObjectArgument ()});
457
+ }
458
+ // Note: A more general VisitCallExpr could also be used here.
459
+ }
460
+
436
461
void VisitCXXNullPtrLiteralExpr (const CXXNullPtrLiteralExpr *N) {
437
462
// / TODO: Handle nullptr expr as a special 'null' loan. Uninitialized
438
463
// / pointers can use the same type of loan.
@@ -492,10 +517,27 @@ class FactGeneratorVisitor : public ConstStmtVisitor<FactGeneratorVisitor> {
492
517
void VisitCXXFunctionalCastExpr (const CXXFunctionalCastExpr *FCE) {
493
518
// Check if this is a test point marker. If so, we are done with this
494
519
// expression.
495
- if (VisitTestPoint (FCE))
520
+ if (handleTestPoint (FCE))
496
521
return ;
497
- // Visit as normal otherwise.
498
- Base::VisitCXXFunctionalCastExpr (FCE);
522
+ if (isGslPointerType (FCE->getType ()))
523
+ addAssignOriginFact (*FCE, *FCE->getSubExpr ());
524
+ }
525
+
526
+ void VisitInitListExpr (const InitListExpr *ILE) {
527
+ if (!hasOrigin (ILE->getType ()))
528
+ return ;
529
+ // For list initialization with a single element, like `View{...}`, the
530
+ // origin of the list itself is the origin of its single element.
531
+ if (ILE->getNumInits () == 1 )
532
+ addAssignOriginFact (*ILE, *ILE->getInit (0 ));
533
+ }
534
+
535
+ void VisitMaterializeTemporaryExpr (const MaterializeTemporaryExpr *MTE) {
536
+ if (!hasOrigin (MTE->getType ()))
537
+ return ;
538
+ // A temporary object's origin is the same as the origin of the
539
+ // expression that initializes it.
540
+ addAssignOriginFact (*MTE, *MTE->getSubExpr ());
499
541
}
500
542
501
543
void handleDestructor (const CFGAutomaticObjDtor &DtorOpt) {
@@ -521,8 +563,75 @@ class FactGeneratorVisitor : public ConstStmtVisitor<FactGeneratorVisitor> {
521
563
}
522
564
523
565
private:
566
+ static bool isGslPointerType (QualType QT) {
567
+ if (const auto *RD = QT->getAsCXXRecordDecl ()) {
568
+ // We need to check the template definition for specializations.
569
+ if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(RD))
570
+ return CTSD->getSpecializedTemplate ()
571
+ ->getTemplatedDecl ()
572
+ ->hasAttr <PointerAttr>();
573
+ return RD->hasAttr <PointerAttr>();
574
+ }
575
+ return false ;
576
+ }
577
+
524
578
// Check if a type has an origin.
525
- bool hasOrigin (QualType QT) { return QT->isPointerOrReferenceType (); }
579
+ static bool hasOrigin (QualType QT) {
580
+ if (QT->isFunctionPointerType ())
581
+ return false ;
582
+ return QT->isPointerOrReferenceType () || isGslPointerType (QT);
583
+ }
584
+
585
+ // / Checks if a call-like expression creates a borrow by passing a local
586
+ // / value to a reference parameter, creating an IssueFact if it does.
587
+ void checkForBorrows (const Expr *Call, const FunctionDecl *FD,
588
+ ArrayRef<const Expr *> Args) {
589
+ if (!FD)
590
+ return ;
591
+
592
+ for (unsigned I = 0 ; I < Args.size (); ++I) {
593
+ if (I >= FD->getNumParams ())
594
+ break ;
595
+
596
+ const ParmVarDecl *Param = FD->getParamDecl (I);
597
+ const Expr *Arg = Args[I];
598
+
599
+ // This is the core condition for a new borrow: a value type (no origin)
600
+ // is passed to a reference parameter.
601
+ if (Param->getType ()->isReferenceType () && !hasOrigin (Arg->getType ())) {
602
+ if (const Loan *L = createLoanFrom (Arg, Call)) {
603
+ OriginID OID = FactMgr.getOriginMgr ().getOrCreate (*Call);
604
+ CurrentBlockFacts.push_back (
605
+ FactMgr.createFact <IssueFact>(L->ID , OID));
606
+ // For view creation, we assume the first borrow is the significant
607
+ // one.
608
+ return ;
609
+ }
610
+ }
611
+ }
612
+ }
613
+
614
+ // / Attempts to create a loan by analyzing the source expression of a borrow.
615
+ // / This method is the single point for creating loans, allowing for future
616
+ // / expansion to handle temporaries, field members, etc.
617
+ // / \param SourceExpr The expression representing the object being borrowed
618
+ // / from.
619
+ // / \param IssueExpr The expression that triggers the borrow (e.g., a
620
+ // / constructor call).
621
+ // / \return The new Loan on success, nullptr on failure.
622
+ const Loan *createLoanFrom (const Expr *SourceExpr, const Expr *IssueExpr) {
623
+ // For now, we only handle direct borrows from local variables.
624
+ // In the future, this can be extended to handle MaterializeTemporaryExpr,
625
+ // etc.
626
+ if (const auto *DRE =
627
+ dyn_cast<DeclRefExpr>(SourceExpr->IgnoreParenImpCasts ())) {
628
+ if (const auto *VD = dyn_cast<ValueDecl>(DRE->getDecl ())) {
629
+ AccessPath Path (VD);
630
+ return &FactMgr.getLoanMgr ().addLoan (Path, IssueExpr);
631
+ }
632
+ }
633
+ return nullptr ;
634
+ }
526
635
527
636
template <typename Destination, typename Source>
528
637
void addAssignOriginFact (const Destination &D, const Source &S) {
@@ -534,7 +643,7 @@ class FactGeneratorVisitor : public ConstStmtVisitor<FactGeneratorVisitor> {
534
643
535
644
// / Checks if the expression is a `void("__lifetime_test_point_...")` cast.
536
645
// / If so, creates a `TestPointFact` and returns true.
537
- bool VisitTestPoint (const CXXFunctionalCastExpr *FCE) {
646
+ bool handleTestPoint (const CXXFunctionalCastExpr *FCE) {
538
647
if (!FCE->getType ()->isVoidType ())
539
648
return false ;
540
649
@@ -612,10 +721,13 @@ class FactGenerator : public RecursiveASTVisitor<FactGenerator> {
612
721
for (const CFGBlock *Block : *AC.getAnalysis <PostOrderCFGView>()) {
613
722
FactGeneratorBlockRAII BlockGenerator (FG, Block);
614
723
for (const CFGElement &Element : *Block) {
615
- if (std::optional<CFGStmt> CS = Element.getAs <CFGStmt>())
724
+ if (std::optional<CFGStmt> CS = Element.getAs <CFGStmt>()) {
725
+ DEBUG_WITH_TYPE (" PrintCFG" , llvm::dbgs () << " ================== \n " );
726
+ DEBUG_WITH_TYPE (" PrintCFG" , CS->dump ());
727
+ DEBUG_WITH_TYPE (" PrintCFG" , CS->getStmt ()->dumpColor ());
616
728
TraverseStmt (const_cast <Stmt *>(CS->getStmt ()));
617
- else if (std::optional<CFGAutomaticObjDtor> DtorOpt =
618
- Element.getAs <CFGAutomaticObjDtor>())
729
+ } else if (std::optional<CFGAutomaticObjDtor> DtorOpt =
730
+ Element.getAs <CFGAutomaticObjDtor>())
619
731
FG.handleDestructor (*DtorOpt);
620
732
}
621
733
}
0 commit comments