https://github.com/jvoung updated https://github.com/llvm/llvm-project/pull/191681
>From 4312d9f4d766578067d0cd3495eb31d9ff2df863 Mon Sep 17 00:00:00 2001 From: Jan Voung <[email protected]> Date: Sun, 12 Apr 2026 03:16:45 +0000 Subject: [PATCH 1/2] Fix registered matcher for bugprone-unchecked-optional-access (recent changes to libcxx) Further fix for #187788. Previous attempt in PR 188044 only updated the model and model tests, but forgot to update the registered matcher. --- .../bugprone/UncheckedOptionalAccessCheck.cpp | 5 ++- .../std/types/optional.h | 39 +++++++++++++------ .../bugprone/unchecked-optional-access.cpp | 35 ++++++++++++++--- .../Models/UncheckedOptionalAccessModel.h | 5 ++- .../Models/UncheckedOptionalAccessModel.cpp | 11 ++++-- 5 files changed, 71 insertions(+), 24 deletions(-) diff --git a/clang-tools-extra/clang-tidy/bugprone/UncheckedOptionalAccessCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/UncheckedOptionalAccessCheck.cpp index 79d6e4974c316..cf7829984fab9 100644 --- a/clang-tools-extra/clang-tidy/bugprone/UncheckedOptionalAccessCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/UncheckedOptionalAccessCheck.cpp @@ -27,8 +27,9 @@ static constexpr StringRef FuncID = "fun"; void UncheckedOptionalAccessCheck::registerMatchers(MatchFinder *Finder) { using namespace ast_matchers; - auto HasOptionalCallDescendant = hasDescendant(callExpr(callee(cxxMethodDecl( - ofClass(UncheckedOptionalAccessModel::optionalClassDecl()))))); + auto HasOptionalCallDescendant = hasDescendant(callExpr( + anyOf(UncheckedOptionalAccessModel::memberCallToOptionalClass(), + UncheckedOptionalAccessModel::operatorCallToOptionalClass()))); Finder->addMatcher( decl(anyOf(functionDecl( // FIXME: Remove the filter below when lambdas are diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/unchecked-optional-access/std/types/optional.h b/clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/unchecked-optional-access/std/types/optional.h index d331585a4c21c..c508b2abe6e47 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/unchecked-optional-access/std/types/optional.h +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/unchecked-optional-access/std/types/optional.h @@ -1,6 +1,7 @@ #ifndef LLVM_CLANG_TOOLS_EXTRA_TEST_CLANG_TIDY_CHECKERS_INPUTS_STD_TYPES_OPTIONAL_H_ #define LLVM_CLANG_TOOLS_EXTRA_TEST_CLANG_TIDY_CHECKERS_INPUTS_STD_TYPES_OPTIONAL_H_ +/// Mock of `std::optional`. namespace std { struct nullopt_t { @@ -9,16 +10,13 @@ struct nullopt_t { constexpr nullopt_t nullopt; -template <typename T> -class optional { -public: - constexpr optional() noexcept; - - constexpr optional(nullopt_t) noexcept; - - optional(const optional &) = default; +template <class T> struct __optional_destruct_base { + constexpr void reset() noexcept; +}; - optional(optional &&) = default; +template <class T> +struct __optional_storage_base : __optional_destruct_base<T> { + constexpr bool has_value() const noexcept; const T &operator*() const &; T &operator*() &; @@ -32,9 +30,28 @@ class optional { T &value() &; const T &&value() const &&; T &&value() &&; +}; + +// Note: the inheritance may or may not be private: +// https://github.com/llvm/llvm-project/issues/187788 +template <typename T> class optional : public __optional_storage_base<T> { + using base = __optional_storage_base<T>; + +public: + constexpr optional() noexcept; + + constexpr optional(nullopt_t) noexcept; + + optional(const optional &) = default; + + optional(optional &&) = default; + + using base::operator*; + using base::operator->; + using base::value; constexpr explicit operator bool() const noexcept; - constexpr bool has_value() const noexcept; + using base::has_value; template <typename U> constexpr T value_or(U &&v) const &; @@ -44,7 +61,7 @@ class optional { template <typename... Args> T &emplace(Args &&...args); - void reset() noexcept; + using base::reset; void swap(optional &rhs) noexcept; diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/unchecked-optional-access.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/unchecked-optional-access.cpp index 214072de772e2..e4d59528134c0 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/unchecked-optional-access.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/unchecked-optional-access.cpp @@ -1,16 +1,28 @@ // RUN: %check_clang_tidy %s bugprone-unchecked-optional-access %t -- -- -I %S/Inputs/unchecked-optional-access #include "absl/types/optional.h" -#include "folly/types/Optional.h" -#include "bde/types/bsl_optional.h" #include "bde/types/bdlb_nullablevalue.h" +#include "bde/types/bsl_optional.h" +#include "folly/types/Optional.h" +#include "std/types/optional.h" -void unchecked_value_access(const absl::optional<int> &opt) { +void unchecked_value_access(std::optional<int> opt) { opt.value(); // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: unchecked access to optional value [bugprone-unchecked-optional-access] } -void unchecked_deref_operator_access(const absl::optional<int> &opt) { +void absl_unchecked_value_access(const absl::optional<int> &opt) { + opt.value(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: unchecked access to optional value + // [bugprone-unchecked-optional-access] +} + +void unchecked_deref_operator_access(std::optional<int> opt) { + *opt; + // CHECK-MESSAGES: :[[@LINE-1]]:4: warning: unchecked access to optional value +} + +void absl_unchecked_deref_operator_access(const absl::optional<int> &opt) { *opt; // CHECK-MESSAGES: :[[@LINE-1]]:4: warning: unchecked access to optional value } @@ -19,7 +31,12 @@ struct Foo { void foo() const {} }; -void unchecked_arrow_operator_access(const absl::optional<Foo> &opt) { +void unchecked_arrow_operator_access(std::optional<Foo> opt) { + opt->foo(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: unchecked access to optional value +} + +void absl_unchecked_arrow_operator_access(const absl::optional<Foo> &opt) { opt->foo(); // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: unchecked access to optional value } @@ -40,7 +57,13 @@ void folly_value_after_swap(folly::Optional<int> opt1, folly::Optional<int> opt2 } } -void checked_access(const absl::optional<int> &opt) { +void checked_access(std::optional<int> opt) { + if (opt.has_value()) { + opt.value(); + } +} + +void absl_checked_access(const absl::optional<int> &opt) { if (opt.has_value()) { opt.value(); } diff --git a/clang/include/clang/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.h b/clang/include/clang/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.h index c547d6ce2e387..e7f1f407d9912 100644 --- a/clang/include/clang/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.h +++ b/clang/include/clang/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.h @@ -62,8 +62,9 @@ class UncheckedOptionalAccessModel public: UncheckedOptionalAccessModel(ASTContext &Ctx, dataflow::Environment &Env); - /// Returns a matcher for the optional classes covered by this model. - static ast_matchers::DeclarationMatcher optionalClassDecl(); + /// Returns a matcher for calls to optional classes diagnosed by this model. + static ast_matchers::StatementMatcher memberCallToOptionalClass(); + static ast_matchers::StatementMatcher operatorCallToOptionalClass(); static UncheckedOptionalAccessLattice initialElement() { return {}; } diff --git a/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp b/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp index 182db36be3513..568564fb361f4 100644 --- a/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp +++ b/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp @@ -1278,9 +1278,14 @@ auto buildDiagnoseMatchSwitch( } // namespace -ast_matchers::DeclarationMatcher -UncheckedOptionalAccessModel::optionalClassDecl() { - return cxxRecordDecl(optionalClass()); +ast_matchers::StatementMatcher +UncheckedOptionalAccessModel::memberCallToOptionalClass() { + return cxxMemberCallExpr(hasOptionalReceiverType()); +} + +ast_matchers::StatementMatcher +UncheckedOptionalAccessModel::operatorCallToOptionalClass() { + return cxxOperatorCallExpr(hasOptionalOperatorObjectType()); } UncheckedOptionalAccessModel::UncheckedOptionalAccessModel(ASTContext &Ctx, >From 092b8ac39a034f2ec7539f69177940afdc36038b Mon Sep 17 00:00:00 2001 From: Jan Voung <[email protected]> Date: Mon, 13 Apr 2026 14:38:43 +0000 Subject: [PATCH 2/2] Fix test CHECK --- .../clang-tidy/checkers/bugprone/unchecked-optional-access.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/unchecked-optional-access.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/unchecked-optional-access.cpp index e4d59528134c0..337474bdf7535 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/unchecked-optional-access.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/unchecked-optional-access.cpp @@ -13,8 +13,7 @@ void unchecked_value_access(std::optional<int> opt) { void absl_unchecked_value_access(const absl::optional<int> &opt) { opt.value(); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: unchecked access to optional value - // [bugprone-unchecked-optional-access] + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: unchecked access to optional value [bugprone-unchecked-optional-access] } void unchecked_deref_operator_access(std::optional<int> opt) { _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
