17
17
#include " clang/AST/Expr.h"
18
18
#include " clang/AST/ExprCXX.h"
19
19
#include " clang/AST/Stmt.h"
20
+ #include " clang/AST/Type.h"
20
21
#include " clang/ASTMatchers/ASTMatchers.h"
21
22
#include " clang/ASTMatchers/ASTMatchersMacros.h"
22
23
#include " clang/Analysis/CFG.h"
23
24
#include " clang/Analysis/FlowSensitive/CFGMatchSwitch.h"
24
25
#include " clang/Analysis/FlowSensitive/DataflowEnvironment.h"
25
26
#include " clang/Analysis/FlowSensitive/Formula.h"
26
- #include " clang/Analysis/FlowSensitive/NoopLattice .h"
27
+ #include " clang/Analysis/FlowSensitive/RecordOps .h"
27
28
#include " clang/Analysis/FlowSensitive/StorageLocation.h"
28
29
#include " clang/Analysis/FlowSensitive/Value.h"
29
30
#include " clang/Basic/SourceLocation.h"
@@ -104,10 +105,17 @@ static const CXXRecordDecl *getOptionalBaseClass(const CXXRecordDecl *RD) {
104
105
return nullptr ;
105
106
}
106
107
108
+ static bool isSupportedOptionalType (QualType Ty) {
109
+ const CXXRecordDecl *Optional =
110
+ getOptionalBaseClass (Ty->getAsCXXRecordDecl ());
111
+ return Optional != nullptr ;
112
+ }
113
+
107
114
namespace {
108
115
109
116
using namespace ::clang::ast_matchers;
110
- using LatticeTransferState = TransferState<NoopLattice>;
117
+
118
+ using LatticeTransferState = TransferState<UncheckedOptionalAccessLattice>;
111
119
112
120
AST_MATCHER (CXXRecordDecl, optionalClass) { return hasOptionalClassName (Node); }
113
121
@@ -325,6 +333,19 @@ auto isValueOrNotEqX() {
325
333
ComparesToSame (integerLiteral (equals (0 )))));
326
334
}
327
335
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
+
328
349
auto isCallReturningOptional () {
329
350
return callExpr (hasType (qualType (
330
351
anyOf (desugarsToOptionalOrDerivedType (),
@@ -523,6 +544,99 @@ void transferCallReturningOptional(const CallExpr *E,
523
544
setHasValue (*Loc, State.Env .makeAtomicBoolValue (), State.Env );
524
545
}
525
546
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
+
526
640
void constructOptionalValue (const Expr &E, Environment &Env,
527
641
BoolValue &HasValueVal) {
528
642
RecordStorageLocation &Loc = Env.getResultObjectLocation (E);
@@ -899,7 +1013,17 @@ auto buildTransferMatchSwitch() {
899
1013
transferOptionalAndValueCmp (Cmp, Cmp->getArg (1 ), State.Env );
900
1014
})
901
1015
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
903
1027
.CaseOfCFGStmt <CallExpr>(isCallReturningOptional (),
904
1028
transferCallReturningOptional)
905
1029
@@ -958,7 +1082,8 @@ UncheckedOptionalAccessModel::optionalClassDecl() {
958
1082
959
1083
UncheckedOptionalAccessModel::UncheckedOptionalAccessModel (ASTContext &Ctx,
960
1084
Environment &Env)
961
- : DataflowAnalysis<UncheckedOptionalAccessModel, NoopLattice>(Ctx),
1085
+ : DataflowAnalysis<UncheckedOptionalAccessModel,
1086
+ UncheckedOptionalAccessLattice>(Ctx),
962
1087
TransferMatchSwitch (buildTransferMatchSwitch()) {
963
1088
Env.getDataflowAnalysisContext ().setSyntheticFieldCallback (
964
1089
[&Ctx](QualType Ty) -> llvm::StringMap<QualType> {
@@ -972,7 +1097,8 @@ UncheckedOptionalAccessModel::UncheckedOptionalAccessModel(ASTContext &Ctx,
972
1097
}
973
1098
974
1099
void UncheckedOptionalAccessModel::transfer (const CFGElement &Elt,
975
- NoopLattice &L, Environment &Env) {
1100
+ UncheckedOptionalAccessLattice &L,
1101
+ Environment &Env) {
976
1102
LatticeTransferState State (L, Env);
977
1103
TransferMatchSwitch (Elt, getASTContext (), State);
978
1104
}
0 commit comments