aboutsummaryrefslogtreecommitdiff
path: root/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
diff options
context:
space:
mode:
authorJan Voung <jvoung@google.com>2025-03-07 08:16:46 -0500
committerGitHub <noreply@github.com>2025-03-07 08:16:46 -0500
commit81168e2dc1c2069178b1acd5e302be99d7fb0e45 (patch)
tree71d8b03bea32e705a9c0e6e79e23d9ea6fbccef9 /clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
parent9d191f11825875cb21363b1e1a9303e06fa1316c (diff)
downloadllvm-81168e2dc1c2069178b1acd5e302be99d7fb0e45.zip
llvm-81168e2dc1c2069178b1acd5e302be99d7fb0e45.tar.gz
llvm-81168e2dc1c2069178b1acd5e302be99d7fb0e45.tar.bz2
[clang][dataflow] Add test for crash repro and clean up const accessor handling (#129930)
Add test for https://github.com/llvm/llvm-project/issues/125589 The crash is actually incidentally fixed by https://github.com/llvm/llvm-project/pull/128437 since it added a branch for the reference case and would no longer fall through when the return type is a reference to a pointer. Clean up a bit as well: - make the fallback for early returns more consistent (check if returning optional and call transfer function for that case) - check RecordLoc == nullptr in one place - clean up extra spaces in test - clean up parameterization in test of `std::` vs `$ns::$`
Diffstat (limited to 'clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp')
-rw-r--r--clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp115
1 files changed, 62 insertions, 53 deletions
diff --git a/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp b/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
index 9381c5c..c28424f 100644
--- a/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
+++ b/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
@@ -551,83 +551,92 @@ void transferCallReturningOptional(const CallExpr *E,
setHasValue(*Loc, State.Env.makeAtomicBoolValue(), State.Env);
}
-void handleConstMemberCall(const CallExpr *CE,
+// Returns true if the const accessor is handled by caching.
+// Returns false if we could not cache. We should perform default handling
+// in that case.
+bool 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())) {
- const FunctionDecl *DirectCallee = CE->getDirectCallee();
- if (DirectCallee == nullptr)
- return;
- StorageLocation &Loc =
- State.Lattice.getOrCreateConstMethodReturnStorageLocation(
- *RecordLoc, DirectCallee, State.Env, [&](StorageLocation &Loc) {
- setHasValue(cast<RecordStorageLocation>(Loc),
- State.Env.makeAtomicBoolValue(), State.Env);
- });
- 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;
- }
+ if (RecordLoc == nullptr)
+ return false;
- // Cache if the const method returns a reference
- if (RecordLoc != nullptr && CE->isGLValue()) {
+ // Cache if the const method returns a reference.
+ if (CE->isGLValue()) {
const FunctionDecl *DirectCallee = CE->getDirectCallee();
if (DirectCallee == nullptr)
- return;
+ return false;
+ // Initialize the optional's "has_value" property to true if the type is
+ // optional, otherwise no-op. If we want to support const ref to pointers or
+ // bools we should initialize their values here too.
+ auto Init = [&](StorageLocation &Loc) {
+ if (isSupportedOptionalType(CE->getType()))
+ setHasValue(cast<RecordStorageLocation>(Loc),
+ State.Env.makeAtomicBoolValue(), State.Env);
+ };
StorageLocation &Loc =
State.Lattice.getOrCreateConstMethodReturnStorageLocation(
- *RecordLoc, DirectCallee, State.Env, [&](StorageLocation &Loc) {
- // no-op
- });
+ *RecordLoc, DirectCallee, State.Env, Init);
State.Env.setStorageLocation(*CE, Loc);
- return;
+ return true;
}
-
- // Cache if the const method returns a boolean or pointer type.
- // We may decide to cache other return types in the future.
- if (RecordLoc != nullptr &&
- (CE->getType()->isBooleanType() || CE->getType()->isPointerType())) {
+ // PRValue cases:
+ if (CE->getType()->isBooleanType() || CE->getType()->isPointerType()) {
+ // If the const method returns a boolean or pointer type.
Value *Val = State.Lattice.getOrCreateConstMethodReturnValue(*RecordLoc, CE,
State.Env);
if (Val == nullptr)
- return;
+ return false;
State.Env.setValue(*CE, *Val);
- return;
+ return true;
}
-
- // 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);
+ // If the const method returns an optional by value.
+ const FunctionDecl *DirectCallee = CE->getDirectCallee();
+ if (DirectCallee == nullptr)
+ return false;
+ StorageLocation &Loc =
+ State.Lattice.getOrCreateConstMethodReturnStorageLocation(
+ *RecordLoc, DirectCallee, State.Env, [&](StorageLocation &Loc) {
+ setHasValue(cast<RecordStorageLocation>(Loc),
+ State.Env.makeAtomicBoolValue(), State.Env);
+ });
+ // 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 true;
}
+
+ return false;
}
-void transferValue_ConstMemberCall(const CXXMemberCallExpr *MCE,
- const MatchFinder::MatchResult &Result,
- LatticeTransferState &State) {
- handleConstMemberCall(
+void handleConstMemberCallWithFallbacks(
+ const CallExpr *CE, dataflow::RecordStorageLocation *RecordLoc,
+ const MatchFinder::MatchResult &Result, LatticeTransferState &State) {
+ if (handleConstMemberCall(CE, RecordLoc, Result, State))
+ return;
+ // Perform default handling if the call returns an optional, but wasn't
+ // handled by caching.
+ if (isSupportedOptionalType(CE->getType()))
+ transferCallReturningOptional(CE, Result, State);
+}
+
+void transferConstMemberCall(const CXXMemberCallExpr *MCE,
+ const MatchFinder::MatchResult &Result,
+ LatticeTransferState &State) {
+ handleConstMemberCallWithFallbacks(
MCE, dataflow::getImplicitObjectLocation(*MCE, State.Env), Result, State);
}
-void transferValue_ConstMemberOperatorCall(
- const CXXOperatorCallExpr *OCE, const MatchFinder::MatchResult &Result,
- LatticeTransferState &State) {
+void transferConstMemberOperatorCall(const CXXOperatorCallExpr *OCE,
+ const MatchFinder::MatchResult &Result,
+ LatticeTransferState &State) {
auto *RecordLoc = cast_or_null<dataflow::RecordStorageLocation>(
State.Env.getStorageLocation(*OCE->getArg(0)));
- handleConstMemberCall(OCE, RecordLoc, Result, State);
+ handleConstMemberCallWithFallbacks(OCE, RecordLoc, Result, State);
}
void handleNonConstMemberCall(const CallExpr *CE,
@@ -1094,9 +1103,9 @@ auto buildTransferMatchSwitch() {
// const accessor calls
.CaseOfCFGStmt<CXXMemberCallExpr>(isZeroParamConstMemberCall(),
- transferValue_ConstMemberCall)
+ transferConstMemberCall)
.CaseOfCFGStmt<CXXOperatorCallExpr>(isZeroParamConstMemberOperatorCall(),
- transferValue_ConstMemberOperatorCall)
+ transferConstMemberOperatorCall)
// non-const member calls that may modify the state of an object.
.CaseOfCFGStmt<CXXMemberCallExpr>(isNonConstMemberCall(),
transferValue_NonConstMemberCall)