diff options
author | Chris Cotter <ccotter14@bloomberg.net> | 2024-09-25 10:54:31 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-09-25 10:54:31 -0400 |
commit | 11c423f9bebc3be27722225ca8120e8775be836c (patch) | |
tree | e05542cc2bb689bd45572783c244a79caf2916d5 /clang/lib | |
parent | 97189492a1a75d39c09b0a54982f2a028c9bd652 (diff) | |
download | llvm-11c423f9bebc3be27722225ca8120e8775be836c.zip llvm-11c423f9bebc3be27722225ca8120e8775be836c.tar.gz llvm-11c423f9bebc3be27722225ca8120e8775be836c.tar.bz2 |
[clang-tidy] Add support for bsl::optional (#101450)
Diffstat (limited to 'clang/lib')
-rw-r--r-- | clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp | 67 |
1 files changed, 54 insertions, 13 deletions
diff --git a/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp b/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp index 0707aa6..70ffe92 100644 --- a/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp +++ b/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp @@ -38,10 +38,25 @@ namespace clang { namespace dataflow { -static bool isTopLevelNamespaceWithName(const NamespaceDecl &NS, - llvm::StringRef Name) { - return NS.getDeclName().isIdentifier() && NS.getName() == Name && - NS.getParent() != nullptr && NS.getParent()->isTranslationUnit(); +// Note: the Names appear in reverse order. E.g., to check +// if NS is foo::bar::, call isFullyQualifiedNamespaceEqualTo(NS, "bar", "foo") +template <class... NameTypes> +static bool isFullyQualifiedNamespaceEqualTo(const NamespaceDecl &NS, + llvm::StringRef Name, + NameTypes... Names) { + if (!(NS.getDeclName().isIdentifier() && NS.getName() == Name && + NS.getParent() != nullptr)) + return false; + + if constexpr (sizeof...(NameTypes) > 0) { + if (NS.getParent()->isTranslationUnit()) + return false; + if (const auto *NextNS = dyn_cast_or_null<NamespaceDecl>(NS.getParent())) + return isFullyQualifiedNamespaceEqualTo(*NextNS, Names...); + return false; + } else { + return NS.getParent()->isTranslationUnit(); + } } static bool hasOptionalClassName(const CXXRecordDecl &RD) { @@ -50,15 +65,23 @@ static bool hasOptionalClassName(const CXXRecordDecl &RD) { if (RD.getName() == "optional") { if (const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.getDeclContext())) - return N->isStdNamespace() || isTopLevelNamespaceWithName(*N, "absl"); + return N->isStdNamespace() || + isFullyQualifiedNamespaceEqualTo(*N, "absl") || + isFullyQualifiedNamespaceEqualTo(*N, "bsl"); return false; } if (RD.getName() == "Optional") { // Check whether namespace is "::base" or "::folly". const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.getDeclContext()); - return N != nullptr && (isTopLevelNamespaceWithName(*N, "base") || - isTopLevelNamespaceWithName(*N, "folly")); + return N != nullptr && (isFullyQualifiedNamespaceEqualTo(*N, "base") || + isFullyQualifiedNamespaceEqualTo(*N, "folly")); + } + + if (RD.getName() == "NullableValue") { + const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.getDeclContext()); + return N != nullptr && + isFullyQualifiedNamespaceEqualTo(*N, "bdlb", "BloombergLP"); } return false; @@ -195,22 +218,25 @@ auto isOptionalOperatorCallWithName( } auto isMakeOptionalCall() { - return callExpr(callee(functionDecl(hasAnyName( - "std::make_optional", "base::make_optional", - "absl::make_optional", "folly::make_optional"))), - hasOptionalType()); + return callExpr( + callee(functionDecl(hasAnyName( + "std::make_optional", "base::make_optional", "absl::make_optional", + "folly::make_optional", "bsl::make_optional"))), + hasOptionalType()); } auto nulloptTypeDecl() { return namedDecl(hasAnyName("std::nullopt_t", "absl::nullopt_t", - "base::nullopt_t", "folly::None")); + "base::nullopt_t", "folly::None", + "bsl::nullopt_t")); } auto hasNulloptType() { return hasType(nulloptTypeDecl()); } auto inPlaceClass() { return recordDecl(hasAnyName("std::in_place_t", "absl::in_place_t", - "base::in_place_t", "folly::in_place_t")); + "base::in_place_t", "folly::in_place_t", + "bsl::in_place_t")); } auto isOptionalNulloptConstructor() { @@ -415,6 +441,15 @@ void transferOptionalHasValueCall(const CXXMemberCallExpr *CallExpr, } } +void transferOptionalIsNullCall(const CXXMemberCallExpr *CallExpr, + const MatchFinder::MatchResult &, + LatticeTransferState &State) { + if (auto *HasValueVal = getHasValue( + State.Env, getImplicitObjectLocation(*CallExpr, State.Env))) { + State.Env.setValue(*CallExpr, State.Env.makeNot(*HasValueVal)); + } +} + /// `ModelPred` builds a logical formula relating the predicate in /// `ValueOrPredExpr` to the optional's `has_value` property. void transferValueOrImpl( @@ -784,6 +819,12 @@ auto buildTransferMatchSwitch() { isOptionalMemberCallWithNameMatcher(hasName("operator bool")), transferOptionalHasValueCall) + // NullableValue::isNull + // Only NullableValue has isNull + .CaseOfCFGStmt<CXXMemberCallExpr>( + isOptionalMemberCallWithNameMatcher(hasName("isNull")), + transferOptionalIsNullCall) + // optional::emplace .CaseOfCFGStmt<CXXMemberCallExpr>( isOptionalMemberCallWithNameMatcher(hasName("emplace")), |