aboutsummaryrefslogtreecommitdiff
path: root/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
diff options
context:
space:
mode:
authorAMS21 <AMS21.github@gmail.com>2023-04-04 06:38:40 +0000
committerPiotr Zegar <me@piotrzegar.pl>2023-04-04 07:20:25 +0000
commit25956d55d02489964428ab5f55e609ff16c6632d (patch)
tree21379e2ffb952a47f4f99e1cf200fac09076112f /clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
parent3afe3dbfa0157608aa1d058f6be28e0060aaf9c6 (diff)
downloadllvm-25956d55d02489964428ab5f55e609ff16c6632d.zip
llvm-25956d55d02489964428ab5f55e609ff16c6632d.tar.gz
llvm-25956d55d02489964428ab5f55e609ff16c6632d.tar.bz2
[clang-tidy] Allow bugprone-unchecked-optional-access to handle calls to `std::forward`
The check now understands that calling `std::forward` will not modify the underlying optional value. This fixes llvm#59705 Reviewed By: PiotrZSL Differential Revision: https://reviews.llvm.org/D147383
Diffstat (limited to 'clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp')
-rw-r--r--clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp41
1 files changed, 37 insertions, 4 deletions
diff --git a/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp b/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
index a91ec5d..15120233 100644
--- a/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
+++ b/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
@@ -96,7 +96,6 @@ auto hasAnyOptionalType() {
recordType(hasDeclaration(anyOf(nulloptTypeDecl(), optionalClass())))));
}
-
auto inPlaceClass() {
return recordDecl(
hasAnyName("std::in_place_t", "absl::in_place_t", "base::in_place_t"));
@@ -149,6 +148,11 @@ auto isStdSwapCall() {
hasArgument(1, hasOptionalType()));
}
+auto isStdForwardCall() {
+ return callExpr(callee(functionDecl(hasName("std::forward"))),
+ argumentCountIs(1), hasArgument(0, hasOptionalType()));
+}
+
constexpr llvm::StringLiteral ValueOrCallID = "ValueOrCall";
auto isValueOrStringEmptyCall() {
@@ -571,6 +575,31 @@ void transferStdSwapCall(const CallExpr *E, const MatchFinder::MatchResult &,
transferSwap(*E->getArg(0), SkipPast::Reference, *E->getArg(1), State.Env);
}
+void transferStdForwardCall(const CallExpr *E, const MatchFinder::MatchResult &,
+ LatticeTransferState &State) {
+ assert(E->getNumArgs() == 1);
+
+ StorageLocation *LocRet = State.Env.getStorageLocation(*E, SkipPast::None);
+ if (LocRet != nullptr)
+ return;
+
+ StorageLocation *LocArg =
+ State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference);
+
+ if (LocArg == nullptr)
+ return;
+
+ Value *ValArg = State.Env.getValue(*LocArg);
+ if (ValArg == nullptr)
+ ValArg = &createOptionalValue(State.Env, State.Env.makeAtomicBoolValue());
+
+ // Create a new storage location
+ LocRet = &State.Env.createStorageLocation(*E);
+ State.Env.setStorageLocation(*E, *LocRet);
+
+ State.Env.setValue(*LocRet, *ValArg);
+}
+
BoolValue &evaluateEquality(Environment &Env, BoolValue &EqVal, BoolValue &LHS,
BoolValue &RHS) {
// Logically, an optional<T> object is composed of two values - a `has_value`
@@ -686,7 +715,6 @@ auto buildTransferMatchSwitch() {
.CaseOfCFGStmt<CXXConstructExpr>(isOptionalValueOrConversionConstructor(),
transferValueOrConversionConstructor)
-
// optional::operator=
.CaseOfCFGStmt<CXXOperatorCallExpr>(
isOptionalValueOrConversionAssignment(),
@@ -745,6 +773,9 @@ auto buildTransferMatchSwitch() {
// std::swap
.CaseOfCFGStmt<CallExpr>(isStdSwapCall(), transferStdSwapCall)
+ // std::forward
+ .CaseOfCFGStmt<CallExpr>(isStdForwardCall(), transferStdForwardCall)
+
// opt.value_or("").empty()
.CaseOfCFGStmt<Expr>(isValueOrStringEmptyCall(),
transferValueOrStringEmptyCall)
@@ -845,10 +876,12 @@ ComparisonResult UncheckedOptionalAccessModel::compare(
return ComparisonResult::Unknown;
bool MustNonEmpty1 = isNonEmptyOptional(Val1, Env1);
bool MustNonEmpty2 = isNonEmptyOptional(Val2, Env2);
- if (MustNonEmpty1 && MustNonEmpty2) return ComparisonResult::Same;
+ if (MustNonEmpty1 && MustNonEmpty2)
+ return ComparisonResult::Same;
// If exactly one is true, then they're different, no reason to check whether
// they're definitely empty.
- if (MustNonEmpty1 || MustNonEmpty2) return ComparisonResult::Different;
+ if (MustNonEmpty1 || MustNonEmpty2)
+ return ComparisonResult::Different;
// Check if they're both definitely empty.
return (isEmptyOptional(Val1, Env1) && isEmptyOptional(Val2, Env2))
? ComparisonResult::Same