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,100 @@ 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
+ // 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
+
526
641
void constructOptionalValue (const Expr &E, Environment &Env,
527
642
BoolValue &HasValueVal) {
528
643
RecordStorageLocation &Loc = Env.getResultObjectLocation (E);
@@ -899,7 +1014,17 @@ auto buildTransferMatchSwitch() {
899
1014
transferOptionalAndValueCmp (Cmp, Cmp->getArg (1 ), State.Env );
900
1015
})
901
1016
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
903
1028
.CaseOfCFGStmt <CallExpr>(isCallReturningOptional (),
904
1029
transferCallReturningOptional)
905
1030
@@ -958,7 +1083,8 @@ UncheckedOptionalAccessModel::optionalClassDecl() {
958
1083
959
1084
UncheckedOptionalAccessModel::UncheckedOptionalAccessModel (ASTContext &Ctx,
960
1085
Environment &Env)
961
- : DataflowAnalysis<UncheckedOptionalAccessModel, NoopLattice>(Ctx),
1086
+ : DataflowAnalysis<UncheckedOptionalAccessModel,
1087
+ UncheckedOptionalAccessLattice>(Ctx),
962
1088
TransferMatchSwitch (buildTransferMatchSwitch()) {
963
1089
Env.getDataflowAnalysisContext ().setSyntheticFieldCallback (
964
1090
[&Ctx](QualType Ty) -> llvm::StringMap<QualType> {
@@ -972,7 +1098,8 @@ UncheckedOptionalAccessModel::UncheckedOptionalAccessModel(ASTContext &Ctx,
972
1098
}
973
1099
974
1100
void UncheckedOptionalAccessModel::transfer (const CFGElement &Elt,
975
- NoopLattice &L, Environment &Env) {
1101
+ UncheckedOptionalAccessLattice &L,
1102
+ Environment &Env) {
976
1103
LatticeTransferState State (L, Env);
977
1104
TransferMatchSwitch (Elt, getASTContext (), State);
978
1105
}
0 commit comments