diff options
author | AMS21 <AMS21.github@gmail.com> | 2023-04-04 06:38:40 +0000 |
---|---|---|
committer | Piotr Zegar <me@piotrzegar.pl> | 2023-04-04 07:20:25 +0000 |
commit | 25956d55d02489964428ab5f55e609ff16c6632d (patch) | |
tree | 21379e2ffb952a47f4f99e1cf200fac09076112f /clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp | |
parent | 3afe3dbfa0157608aa1d058f6be28e0060aaf9c6 (diff) | |
download | llvm-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.cpp | 41 |
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 |