1717#include " clang/AST/Expr.h"
1818#include " clang/AST/ExprCXX.h"
1919#include " clang/AST/Stmt.h"
20+ #include " clang/AST/Type.h"
2021#include " clang/ASTMatchers/ASTMatchers.h"
2122#include " clang/ASTMatchers/ASTMatchersMacros.h"
2223#include " clang/Analysis/CFG.h"
2324#include " clang/Analysis/FlowSensitive/CFGMatchSwitch.h"
2425#include " clang/Analysis/FlowSensitive/DataflowEnvironment.h"
2526#include " clang/Analysis/FlowSensitive/Formula.h"
26- #include " clang/Analysis/FlowSensitive/NoopLattice .h"
27+ #include " clang/Analysis/FlowSensitive/RecordOps .h"
2728#include " clang/Analysis/FlowSensitive/StorageLocation.h"
2829#include " clang/Analysis/FlowSensitive/Value.h"
2930#include " clang/Basic/SourceLocation.h"
@@ -104,10 +105,17 @@ static const CXXRecordDecl *getOptionalBaseClass(const CXXRecordDecl *RD) {
104105 return nullptr ;
105106}
106107
108+ static bool isSupportedOptionalType (QualType Ty) {
109+ const CXXRecordDecl *Optional =
110+ getOptionalBaseClass (Ty->getAsCXXRecordDecl ());
111+ return Optional != nullptr ;
112+ }
113+
107114namespace {
108115
109116using namespace ::clang::ast_matchers;
110- using LatticeTransferState = TransferState<NoopLattice>;
117+
118+ using LatticeTransferState = TransferState<UncheckedOptionalAccessLattice>;
111119
112120AST_MATCHER (CXXRecordDecl, optionalClass) { return hasOptionalClassName (Node); }
113121
@@ -325,6 +333,19 @@ auto isValueOrNotEqX() {
325333 ComparesToSame (integerLiteral (equals (0 )))));
326334}
327335
336+ auto isZeroParamConstMemberCall () {
337+ return cxxMemberCallExpr (
338+ callee (cxxMethodDecl (parameterCountIs (0 ), isConst ())));
339+ }
340+
341+ auto isNonConstMemberCall () {
342+ return cxxMemberCallExpr (callee (cxxMethodDecl (unless (isConst ()))));
343+ }
344+
345+ auto isNonConstMemberOperatorCall () {
346+ return cxxOperatorCallExpr (callee (cxxMethodDecl (unless (isConst ()))));
347+ }
348+
328349auto isCallReturningOptional () {
329350 return callExpr (hasType (qualType (
330351 anyOf (desugarsToOptionalOrDerivedType (),
@@ -523,6 +544,100 @@ void transferCallReturningOptional(const CallExpr *E,
523544 setHasValue (*Loc, State.Env .makeAtomicBoolValue (), State.Env );
524545}
525546
547+ void handleConstMemberCall (const CallExpr *CE,
548+ dataflow::RecordStorageLocation *RecordLoc,
549+ const MatchFinder::MatchResult &Result,
550+ LatticeTransferState &State) {
551+ // If the const method returns an optional or reference to an optional.
552+ if (RecordLoc != nullptr && isSupportedOptionalType (CE->getType ())) {
553+ StorageLocation *Loc =
554+ State.Lattice .getOrCreateConstMethodReturnStorageLocation (
555+ *RecordLoc, CE, State.Env , [&](StorageLocation &Loc) {
556+ setHasValue (cast<RecordStorageLocation>(Loc),
557+ State.Env .makeAtomicBoolValue (), State.Env );
558+ });
559+ if (Loc == nullptr )
560+ return ;
561+ if (CE->isGLValue ()) {
562+ // If the call to the const method returns a reference to an optional,
563+ // we can use link the call expression to the optional via
564+ // setStorageLocation.
565+ State.Env .setStorageLocation (*CE, *Loc);
566+ } else {
567+ // If the call to the const method returns an optional by value, we
568+ // need to use CopyRecord to link the optional to the result object
569+ // of the call expression.
570+ auto &ResultLoc = State.Env .getResultObjectLocation (*CE);
571+ copyRecord (*cast<RecordStorageLocation>(Loc), ResultLoc, State.Env );
572+ }
573+ return ;
574+ }
575+
576+ // Cache if the const method returns a boolean type.
577+ // We may decide to cache other return types in the future.
578+ if (RecordLoc != nullptr && CE->getType ()->isBooleanType ()) {
579+ Value *Val = State.Lattice .getOrCreateConstMethodReturnValue (*RecordLoc, CE,
580+ State.Env );
581+ if (Val == nullptr )
582+ return ;
583+ State.Env .setValue (*CE, *Val);
584+ return ;
585+ }
586+
587+ // Perform default handling if the call returns an optional
588+ // but wasn't handled above (if RecordLoc is nullptr).
589+ if (isSupportedOptionalType (CE->getType ())) {
590+ transferCallReturningOptional (CE, Result, State);
591+ }
592+ }
593+
594+ void transferValue_ConstMemberCall (const CXXMemberCallExpr *MCE,
595+ const MatchFinder::MatchResult &Result,
596+ LatticeTransferState &State) {
597+ handleConstMemberCall (
598+ MCE, dataflow::getImplicitObjectLocation (*MCE, State.Env ), Result, State);
599+ }
600+
601+ void handleNonConstMemberCall (const CallExpr *CE,
602+ dataflow::RecordStorageLocation *RecordLoc,
603+ const MatchFinder::MatchResult &Result,
604+ LatticeTransferState &State) {
605+ // When a non-const member function is called, reset some state.
606+ if (RecordLoc != nullptr ) {
607+ for (const auto [Field, FieldLoc] : RecordLoc->children ()) {
608+ if (isSupportedOptionalType (Field->getType ())) {
609+ auto *FieldRecordLoc = cast_or_null<RecordStorageLocation>(FieldLoc);
610+ if (FieldRecordLoc) {
611+ setHasValue (*FieldRecordLoc, State.Env .makeAtomicBoolValue (),
612+ State.Env );
613+ }
614+ }
615+ }
616+ State.Lattice .clearConstMethodReturnValues (*RecordLoc);
617+ State.Lattice .clearConstMethodReturnStorageLocations (*RecordLoc);
618+ }
619+
620+ // Perform default handling if the call returns an optional.
621+ if (isSupportedOptionalType (CE->getType ())) {
622+ transferCallReturningOptional (CE, Result, State);
623+ }
624+ }
625+
626+ void transferValue_NonConstMemberCall (const CXXMemberCallExpr *MCE,
627+ const MatchFinder::MatchResult &Result,
628+ LatticeTransferState &State) {
629+ handleNonConstMemberCall (
630+ MCE, dataflow::getImplicitObjectLocation (*MCE, State.Env ), Result, State);
631+ }
632+
633+ void transferValue_NonConstMemberOperatorCall (
634+ const CXXOperatorCallExpr *OCE, const MatchFinder::MatchResult &Result,
635+ LatticeTransferState &State) {
636+ auto *RecordLoc = cast_or_null<dataflow::RecordStorageLocation>(
637+ State.Env .getStorageLocation (*OCE->getArg (0 )));
638+ handleNonConstMemberCall (OCE, RecordLoc, Result, State);
639+ }
640+
526641void constructOptionalValue (const Expr &E, Environment &Env,
527642 BoolValue &HasValueVal) {
528643 RecordStorageLocation &Loc = Env.getResultObjectLocation (E);
@@ -899,7 +1014,17 @@ auto buildTransferMatchSwitch() {
8991014 transferOptionalAndValueCmp (Cmp, Cmp->getArg (1 ), State.Env );
9001015 })
9011016
902- // returns optional
1017+ // const accessor calls
1018+ .CaseOfCFGStmt <CXXMemberCallExpr>(isZeroParamConstMemberCall (),
1019+ transferValue_ConstMemberCall)
1020+ // non-const member calls that may modify the state of an object.
1021+ .CaseOfCFGStmt <CXXMemberCallExpr>(isNonConstMemberCall (),
1022+ transferValue_NonConstMemberCall)
1023+ .CaseOfCFGStmt <CXXOperatorCallExpr>(
1024+ isNonConstMemberOperatorCall (),
1025+ transferValue_NonConstMemberOperatorCall)
1026+
1027+ // other cases of returning optional
9031028 .CaseOfCFGStmt <CallExpr>(isCallReturningOptional (),
9041029 transferCallReturningOptional)
9051030
@@ -958,7 +1083,8 @@ UncheckedOptionalAccessModel::optionalClassDecl() {
9581083
9591084UncheckedOptionalAccessModel::UncheckedOptionalAccessModel (ASTContext &Ctx,
9601085 Environment &Env)
961- : DataflowAnalysis<UncheckedOptionalAccessModel, NoopLattice>(Ctx),
1086+ : DataflowAnalysis<UncheckedOptionalAccessModel,
1087+ UncheckedOptionalAccessLattice>(Ctx),
9621088 TransferMatchSwitch (buildTransferMatchSwitch()) {
9631089 Env.getDataflowAnalysisContext ().setSyntheticFieldCallback (
9641090 [&Ctx](QualType Ty) -> llvm::StringMap<QualType> {
@@ -972,7 +1098,8 @@ UncheckedOptionalAccessModel::UncheckedOptionalAccessModel(ASTContext &Ctx,
9721098}
9731099
9741100void UncheckedOptionalAccessModel::transfer (const CFGElement &Elt,
975- NoopLattice &L, Environment &Env) {
1101+ UncheckedOptionalAccessLattice &L,
1102+ Environment &Env) {
9761103 LatticeTransferState State (L, Env);
9771104 TransferMatchSwitch (Elt, getASTContext (), State);
9781105}
0 commit comments