@@ -428,6 +428,31 @@ class FactGeneratorVisitor : public ConstStmtVisitor<FactGeneratorVisitor> {
428
428
addAssignOriginFact (*VD, *InitExpr);
429
429
}
430
430
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
+
431
456
void VisitCXXNullPtrLiteralExpr (const CXXNullPtrLiteralExpr *N) {
432
457
// / TODO: Handle nullptr expr as a special 'null' loan. Uninitialized
433
458
// / pointers can use the same type of loan.
@@ -479,29 +504,39 @@ class FactGeneratorVisitor : public ConstStmtVisitor<FactGeneratorVisitor> {
479
504
}
480
505
481
506
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 ));
496
514
}
497
515
498
516
void VisitCXXFunctionalCastExpr (const CXXFunctionalCastExpr *FCE) {
499
517
// Check if this is a test point marker. If so, we are done with this
500
518
// expression.
501
519
if (VisitTestPoint (FCE))
502
520
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 ());
505
540
}
506
541
507
542
void handleDestructor (const CFGAutomaticObjDtor &DtorOpt) {
@@ -527,8 +562,88 @@ class FactGeneratorVisitor : public ConstStmtVisitor<FactGeneratorVisitor> {
527
562
}
528
563
529
564
private:
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
+
530
577
// 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
+ }
532
647
533
648
template <typename Destination, typename Source>
534
649
void addAssignOriginFact (const Destination &D, const Source &S) {
@@ -578,10 +693,13 @@ class FactGenerator : public RecursiveASTVisitor<FactGenerator> {
578
693
for (const CFGBlock *Block : *AC.getAnalysis <PostOrderCFGView>()) {
579
694
FactGeneratorBlockRAII BlockGenerator (FG, Block);
580
695
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 ());
582
700
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>())
585
703
FG.handleDestructor (*DtorOpt);
586
704
}
587
705
}
0 commit comments