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,99 @@ 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+ // link the call expression to the cached StorageLocation.
564+ State.Env .setStorageLocation (*CE, *Loc);
565+ } else {
566+ // If the call to the const method returns an optional by value, we
567+ // need to use CopyRecord to link the optional to the result object
568+ // of the call expression.
569+ auto &ResultLoc = State.Env .getResultObjectLocation (*CE);
570+ copyRecord (*cast<RecordStorageLocation>(Loc), ResultLoc, State.Env );
571+ }
572+ return ;
573+ }
574+
575+ // Cache if the const method returns a boolean type.
576+ // We may decide to cache other return types in the future.
577+ if (RecordLoc != nullptr && CE->getType ()->isBooleanType ()) {
578+ Value *Val = State.Lattice .getOrCreateConstMethodReturnValue (*RecordLoc, CE,
579+ State.Env );
580+ if (Val == nullptr )
581+ return ;
582+ State.Env .setValue (*CE, *Val);
583+ return ;
584+ }
585+
586+ // Perform default handling if the call returns an optional
587+ // but wasn't handled above (if RecordLoc is nullptr).
588+ if (isSupportedOptionalType (CE->getType ())) {
589+ transferCallReturningOptional (CE, Result, State);
590+ }
591+ }
592+
593+ void transferValue_ConstMemberCall (const CXXMemberCallExpr *MCE,
594+ const MatchFinder::MatchResult &Result,
595+ LatticeTransferState &State) {
596+ handleConstMemberCall (
597+ MCE, dataflow::getImplicitObjectLocation (*MCE, State.Env ), Result, State);
598+ }
599+
600+ void handleNonConstMemberCall (const CallExpr *CE,
601+ dataflow::RecordStorageLocation *RecordLoc,
602+ const MatchFinder::MatchResult &Result,
603+ LatticeTransferState &State) {
604+ // When a non-const member function is called, reset some state.
605+ if (RecordLoc != nullptr ) {
606+ for (const auto &[Field, FieldLoc] : RecordLoc->children ()) {
607+ if (isSupportedOptionalType (Field->getType ())) {
608+ auto *FieldRecordLoc = cast_or_null<RecordStorageLocation>(FieldLoc);
609+ if (FieldRecordLoc) {
610+ setHasValue (*FieldRecordLoc, State.Env .makeAtomicBoolValue (),
611+ State.Env );
612+ }
613+ }
614+ }
615+ State.Lattice .clearConstMethodReturnValues (*RecordLoc);
616+ State.Lattice .clearConstMethodReturnStorageLocations (*RecordLoc);
617+ }
618+
619+ // Perform default handling if the call returns an optional.
620+ if (isSupportedOptionalType (CE->getType ())) {
621+ transferCallReturningOptional (CE, Result, State);
622+ }
623+ }
624+
625+ void transferValue_NonConstMemberCall (const CXXMemberCallExpr *MCE,
626+ const MatchFinder::MatchResult &Result,
627+ LatticeTransferState &State) {
628+ handleNonConstMemberCall (
629+ MCE, dataflow::getImplicitObjectLocation (*MCE, State.Env ), Result, State);
630+ }
631+
632+ void transferValue_NonConstMemberOperatorCall (
633+ const CXXOperatorCallExpr *OCE, const MatchFinder::MatchResult &Result,
634+ LatticeTransferState &State) {
635+ auto *RecordLoc = cast_or_null<dataflow::RecordStorageLocation>(
636+ State.Env .getStorageLocation (*OCE->getArg (0 )));
637+ handleNonConstMemberCall (OCE, RecordLoc, Result, State);
638+ }
639+
526640void constructOptionalValue (const Expr &E, Environment &Env,
527641 BoolValue &HasValueVal) {
528642 RecordStorageLocation &Loc = Env.getResultObjectLocation (E);
@@ -899,7 +1013,17 @@ auto buildTransferMatchSwitch() {
8991013 transferOptionalAndValueCmp (Cmp, Cmp->getArg (1 ), State.Env );
9001014 })
9011015
902- // returns optional
1016+ // const accessor calls
1017+ .CaseOfCFGStmt <CXXMemberCallExpr>(isZeroParamConstMemberCall (),
1018+ transferValue_ConstMemberCall)
1019+ // non-const member calls that may modify the state of an object.
1020+ .CaseOfCFGStmt <CXXMemberCallExpr>(isNonConstMemberCall (),
1021+ transferValue_NonConstMemberCall)
1022+ .CaseOfCFGStmt <CXXOperatorCallExpr>(
1023+ isNonConstMemberOperatorCall (),
1024+ transferValue_NonConstMemberOperatorCall)
1025+
1026+ // other cases of returning optional
9031027 .CaseOfCFGStmt <CallExpr>(isCallReturningOptional (),
9041028 transferCallReturningOptional)
9051029
@@ -958,7 +1082,8 @@ UncheckedOptionalAccessModel::optionalClassDecl() {
9581082
9591083UncheckedOptionalAccessModel::UncheckedOptionalAccessModel (ASTContext &Ctx,
9601084 Environment &Env)
961- : DataflowAnalysis<UncheckedOptionalAccessModel, NoopLattice>(Ctx),
1085+ : DataflowAnalysis<UncheckedOptionalAccessModel,
1086+ UncheckedOptionalAccessLattice>(Ctx),
9621087 TransferMatchSwitch (buildTransferMatchSwitch()) {
9631088 Env.getDataflowAnalysisContext ().setSyntheticFieldCallback (
9641089 [&Ctx](QualType Ty) -> llvm::StringMap<QualType> {
@@ -972,7 +1097,8 @@ UncheckedOptionalAccessModel::UncheckedOptionalAccessModel(ASTContext &Ctx,
9721097}
9731098
9741099void UncheckedOptionalAccessModel::transfer (const CFGElement &Elt,
975- NoopLattice &L, Environment &Env) {
1100+ UncheckedOptionalAccessLattice &L,
1101+ Environment &Env) {
9761102 LatticeTransferState State (L, Env);
9771103 TransferMatchSwitch (Elt, getASTContext (), State);
9781104}
0 commit comments