@@ -419,10 +419,13 @@ class FactGenerator : public ConstStmtVisitor<FactGenerator> {
419
419
VisitedStmts.clear ();
420
420
for (unsigned I = 0 ; I < Block->size (); ++I) {
421
421
const CFGElement &Element = Block->Elements [I];
422
- if (std::optional<CFGStmt> CS = Element.getAs <CFGStmt>())
422
+ if (std::optional<CFGStmt> CS = Element.getAs <CFGStmt>()) {
423
+ DEBUG_WITH_TYPE (" PrintCFG" , llvm::dbgs () << " ================== \n " );
424
+ DEBUG_WITH_TYPE (" PrintCFG" , CS->dump ());
425
+ DEBUG_WITH_TYPE (" PrintCFG" , CS->getStmt ()->dumpColor ());
423
426
Visit (CS->getStmt ());
424
- else if (std::optional<CFGAutomaticObjDtor> DtorOpt =
425
- Element.getAs <CFGAutomaticObjDtor>())
427
+ } else if (std::optional<CFGAutomaticObjDtor> DtorOpt =
428
+ Element.getAs <CFGAutomaticObjDtor>())
426
429
handleDestructor (*DtorOpt);
427
430
}
428
431
FactMgr.addBlockFacts (Block, CurrentBlockFacts);
@@ -443,6 +446,31 @@ class FactGenerator : public ConstStmtVisitor<FactGenerator> {
443
446
addAssignOriginFact (*VD, *InitExpr);
444
447
}
445
448
449
+ void VisitCXXConstructExpr (const CXXConstructExpr *CCE) {
450
+ if (!isGslPointerType (CCE->getType ()))
451
+ return ;
452
+
453
+ if (CCE->getNumArgs () > 0 && hasOrigin (CCE->getArg (0 )->getType ()))
454
+ // This is a propagation.
455
+ addAssignOriginFact (*CCE, *CCE->getArg (0 ));
456
+ else
457
+ // This could be a new borrow.
458
+ checkForBorrows (CCE, CCE->getConstructor (),
459
+ {CCE->getArgs (), CCE->getNumArgs ()});
460
+ }
461
+
462
+ void VisitCXXMemberCallExpr (const CXXMemberCallExpr *MCE) {
463
+ if (!isGslPointerType (MCE->getImplicitObjectArgument ()->getType ()))
464
+ return ;
465
+ // Specifically for conversion operators, like `std::string_view p = a;`
466
+ if (isa<CXXConversionDecl>(MCE->getCalleeDecl ())) {
467
+ // The argument is the implicit object itself.
468
+ checkForBorrows (MCE, MCE->getMethodDecl (),
469
+ {MCE->getImplicitObjectArgument ()});
470
+ }
471
+ // Note: A more general VisitCallExpr could also be used here.
472
+ }
473
+
446
474
void VisitCXXNullPtrLiteralExpr (const CXXNullPtrLiteralExpr *N) {
447
475
// / TODO: Handle nullptr expr as a special 'null' loan. Uninitialized
448
476
// / pointers can use the same type of loan.
@@ -495,38 +523,131 @@ class FactGenerator : public ConstStmtVisitor<FactGenerator> {
495
523
}
496
524
497
525
void VisitBinaryOperator (const BinaryOperator *BO) {
498
- if (BO->isAssignmentOp ()) {
499
- const Expr *LHSExpr = BO->getLHS ();
500
- const Expr *RHSExpr = BO->getRHS ();
501
-
502
- // We are interested in assignments like `ptr1 = ptr2` or `ptr = &var`
503
- // LHS must be a pointer/reference type that can be an origin.
504
- // RHS must also represent an origin (either another pointer/ref or an
505
- // address-of).
506
- if (const auto *DRE_LHS = dyn_cast<DeclRefExpr>(LHSExpr))
507
- if (const auto *VD_LHS =
508
- dyn_cast<ValueDecl>(DRE_LHS->getDecl ()->getCanonicalDecl ());
509
- VD_LHS && hasOrigin (VD_LHS->getType ()))
510
- addAssignOriginFact (*VD_LHS, *RHSExpr);
511
- }
526
+ if (BO->isAssignmentOp ())
527
+ handleAssignment (BO->getLHS (), BO->getRHS ());
528
+ }
529
+
530
+ void VisitCXXOperatorCallExpr (const CXXOperatorCallExpr *OCE) {
531
+ if (OCE->isAssignmentOp () && OCE->getNumArgs () == 2 )
532
+ handleAssignment (OCE->getArg (0 ), OCE->getArg (1 ));
512
533
}
513
534
514
535
void VisitCXXFunctionalCastExpr (const CXXFunctionalCastExpr *FCE) {
515
536
// Check if this is a test point marker. If so, we are done with this
516
537
// expression.
517
538
if (VisitTestPoint (FCE))
518
539
return ;
519
- // Visit as normal otherwise.
520
- Base::VisitCXXFunctionalCastExpr (FCE);
540
+ if (isGslPointerType (FCE->getType ()))
541
+ addAssignOriginFact (*FCE, *FCE->getSubExpr ());
542
+ }
543
+
544
+ void VisitInitListExpr (const InitListExpr *ILE) {
545
+ if (!hasOrigin (ILE->getType ()))
546
+ return ;
547
+ // For list initialization with a single element, like `View{...}`, the
548
+ // origin of the list itself is the origin of its single element.
549
+ if (ILE->getNumInits () == 1 ) {
550
+ Visit (ILE->getInit (0 ));
551
+ addAssignOriginFact (*ILE, *ILE->getInit (0 ));
552
+ }
553
+ }
554
+
555
+ void VisitMaterializeTemporaryExpr (const MaterializeTemporaryExpr *MTE) {
556
+ if (!hasOrigin (MTE->getType ()))
557
+ return ;
558
+ // A temporary object's origin is the same as the origin of the
559
+ // expression that initializes it.
560
+ addAssignOriginFact (*MTE, *MTE->getSubExpr ());
521
561
}
522
562
523
563
private:
564
+ static bool isGslPointerType (QualType QT) {
565
+ if (const auto *RD = QT->getAsCXXRecordDecl ()) {
566
+ // We need to check the template definition for specializations.
567
+ if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(RD))
568
+ return CTSD->getSpecializedTemplate ()
569
+ ->getTemplatedDecl ()
570
+ ->hasAttr <PointerAttr>();
571
+ return RD->hasAttr <PointerAttr>();
572
+ }
573
+ return false ;
574
+ }
575
+
524
576
// Check if a type has an origin.
525
- bool hasOrigin (QualType QT) { return QT->isPointerOrReferenceType (); }
577
+ static bool hasOrigin (QualType QT) {
578
+ if (QT->isFunctionPointerType ())
579
+ return false ;
580
+ return QT->isPointerOrReferenceType () || isGslPointerType (QT);
581
+ }
582
+
583
+ // / Checks if a call-like expression creates a borrow by passing a local
584
+ // / value to a reference parameter, creating an IssueFact if it does.
585
+ void checkForBorrows (const Expr *Call, const FunctionDecl *FD,
586
+ ArrayRef<const Expr *> Args) {
587
+ if (!FD)
588
+ return ;
589
+
590
+ for (unsigned I = 0 ; I < Args.size (); ++I) {
591
+ if (I >= FD->getNumParams ())
592
+ break ;
593
+
594
+ const ParmVarDecl *Param = FD->getParamDecl (I);
595
+ const Expr *Arg = Args[I];
596
+
597
+ // This is the core condition for a new borrow: a value type (no origin)
598
+ // is passed to a reference parameter.
599
+ if (Param->getType ()->isReferenceType () && !hasOrigin (Arg->getType ())) {
600
+ if (const Loan *L = createLoanFrom (Arg, Call)) {
601
+ OriginID OID = FactMgr.getOriginMgr ().getOrCreate (*Call);
602
+ CurrentBlockFacts.push_back (
603
+ FactMgr.createFact <IssueFact>(L->ID , OID));
604
+ // For view creation, we assume the first borrow is the significant
605
+ // one.
606
+ return ;
607
+ }
608
+ }
609
+ }
610
+ }
611
+
612
+ // / Attempts to create a loan by analyzing the source expression of a borrow.
613
+ // / This method is the single point for creating loans, allowing for future
614
+ // / expansion to handle temporaries, field members, etc.
615
+ // / \param SourceExpr The expression representing the object being borrowed
616
+ // / from.
617
+ // / \param IssueExpr The expression that triggers the borrow (e.g., a
618
+ // / constructor call).
619
+ // / \return The new Loan on success, nullptr on failure.
620
+ const Loan *createLoanFrom (const Expr *SourceExpr, const Expr *IssueExpr) {
621
+ // For now, we only handle direct borrows from local variables.
622
+ // In the future, this can be extended to handle MaterializeTemporaryExpr,
623
+ // etc.
624
+ if (const auto *DRE =
625
+ dyn_cast<DeclRefExpr>(SourceExpr->IgnoreParenImpCasts ())) {
626
+ if (const auto *VD = dyn_cast<ValueDecl>(DRE->getDecl ())) {
627
+ AccessPath Path (VD);
628
+ return &FactMgr.getLoanMgr ().addLoan (Path, IssueExpr);
629
+ }
630
+ }
631
+ return nullptr ;
632
+ }
633
+
634
+ void handleAssignment (const Expr *LHSExpr, const Expr *RHSExpr) {
635
+ // Find the underlying variable declaration for the left-hand side.
636
+ if (const auto *DRE_LHS =
637
+ dyn_cast<DeclRefExpr>(LHSExpr->IgnoreParenImpCasts ()))
638
+ if (const auto *VD_LHS = dyn_cast<ValueDecl>(DRE_LHS->getDecl ()))
639
+ if (hasOrigin (VD_LHS->getType ()))
640
+ // We are interested in assignments like `ptr1 = ptr2` or `ptr = &var`
641
+ // LHS must be a pointer/reference type that can be an origin.
642
+ // RHS must also represent an origin (either another pointer/ref or an
643
+ // address-of).
644
+ addAssignOriginFact (*VD_LHS, *RHSExpr);
645
+ }
526
646
527
647
template <typename Destination, typename Source>
528
648
void addAssignOriginFact (const Destination &D, const Source &S) {
529
649
OriginID DestOID = FactMgr.getOriginMgr ().getOrCreate (D);
650
+ Visit (&S);
530
651
OriginID SrcOID = FactMgr.getOriginMgr ().get (S);
531
652
CurrentBlockFacts.push_back (
532
653
FactMgr.createFact <AssignOriginFact>(DestOID, SrcOID));
0 commit comments