[clang] [clang-tools-extra] [clang][dataflow] Cache accessors for bugprone-unchecked-optional-access (PR #112605)
Yitzhak Mandelbaum via cfe-commits
cfe-commits at lists.llvm.org
Mon Oct 21 08:04:01 PDT 2024
================
@@ -523,6 +544,99 @@ void transferCallReturningOptional(const CallExpr *E,
setHasValue(*Loc, State.Env.makeAtomicBoolValue(), State.Env);
}
+void handleConstMemberCall(const CallExpr *CE,
+ dataflow::RecordStorageLocation *RecordLoc,
+ const MatchFinder::MatchResult &Result,
+ LatticeTransferState &State) {
+ // If the const method returns an optional or reference to an optional.
+ if (RecordLoc != nullptr && isSupportedOptionalType(CE->getType())) {
+ StorageLocation *Loc =
+ State.Lattice.getOrCreateConstMethodReturnStorageLocation(
+ *RecordLoc, CE, State.Env, [&](StorageLocation &Loc) {
+ setHasValue(cast<RecordStorageLocation>(Loc),
+ State.Env.makeAtomicBoolValue(), State.Env);
+ });
+ if (Loc == nullptr)
+ return;
+ if (CE->isGLValue()) {
+ // If the call to the const method returns a reference to an optional,
+ // link the call expression to the cached StorageLocation.
+ State.Env.setStorageLocation(*CE, *Loc);
+ } else {
+ // If the call to the const method returns an optional by value, we
+ // need to use CopyRecord to link the optional to the result object
+ // of the call expression.
+ auto &ResultLoc = State.Env.getResultObjectLocation(*CE);
+ copyRecord(*cast<RecordStorageLocation>(Loc), ResultLoc, State.Env);
+ }
+ return;
+ }
+
+ // Cache if the const method returns a boolean type.
+ // We may decide to cache other return types in the future.
+ if (RecordLoc != nullptr && CE->getType()->isBooleanType()) {
+ Value *Val = State.Lattice.getOrCreateConstMethodReturnValue(*RecordLoc, CE,
+ State.Env);
+ if (Val == nullptr)
+ return;
+ State.Env.setValue(*CE, *Val);
+ return;
+ }
+
+ // Perform default handling if the call returns an optional
+ // but wasn't handled above (if RecordLoc is nullptr).
+ if (isSupportedOptionalType(CE->getType())) {
+ transferCallReturningOptional(CE, Result, State);
+ }
+}
+
+void transferValue_ConstMemberCall(const CXXMemberCallExpr *MCE,
+ const MatchFinder::MatchResult &Result,
+ LatticeTransferState &State) {
+ handleConstMemberCall(
+ MCE, dataflow::getImplicitObjectLocation(*MCE, State.Env), Result, State);
+}
+
+void handleNonConstMemberCall(const CallExpr *CE,
+ dataflow::RecordStorageLocation *RecordLoc,
+ const MatchFinder::MatchResult &Result,
+ LatticeTransferState &State) {
+ // When a non-const member function is called, reset some state.
+ if (RecordLoc != nullptr) {
+ for (const auto [Field, FieldLoc] : RecordLoc->children()) {
+ if (isSupportedOptionalType(Field->getType())) {
+ auto *FieldRecordLoc = cast_or_null<RecordStorageLocation>(FieldLoc);
+ if (FieldRecordLoc) {
+ setHasValue(*FieldRecordLoc, State.Env.makeAtomicBoolValue(),
+ State.Env);
+ }
+ }
+ }
----------------
ymand wrote:
IIUC, this functionality is new and actually independent of the current change? that is, we were not previously clearing object state on non-const method calls, but arguably should have been (for safety purposes)?
If so, is it worth adding a test specifically for this behavior? (that is, checking that an optional field is cleared when a non-const method is called, even without any const-method related behavior)
https://github.com/llvm/llvm-project/pull/112605
More information about the cfe-commits
mailing list