[clang] [llvm] User-defined annotations for clang-tidy analysis POC (PR #195054)
Kay Hicketts via cfe-commits
cfe-commits at lists.llvm.org
Thu Apr 30 04:50:19 PDT 2026
https://github.com/KHicketts updated https://github.com/llvm/llvm-project/pull/195054
>From 52d11c1082709188e7fa4d8b6995febf1bcf19fa Mon Sep 17 00:00:00 2001
From: khickett <khicketts at bloomberg.net>
Date: Tue, 28 Apr 2026 14:37:52 +0100
Subject: [PATCH 1/5] add sema changes for class attribute
---
clang/lib/Sema/SemaDeclAttr.cpp | 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index e47a30193567f..243c66b474384 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -6462,6 +6462,29 @@ static void handleAbiTagAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
AbiTagAttr(S.Context, AL, Tags.data(), Tags.size()));
}
+// for now this only handles std::optional (POC)
+static bool isValidAnalyseAsClassAttr(Decl *D, StringRef Tag) {
+ if (Tag == "std::optional")
+ return true;
+ return false;
+}
+
+static void handleAnalyseAsClass(Sema &S, Decl *D, const ParsedAttr &AL) {
+ StringRef Str;
+ if (!S.checkStringLiteralArgumentAttr(AL, 0, Str))
+ return;
+ if (D->hasAttr<AnalyseAsClassAttr>()) {
+ S.Diag(AL.getLoc(), diag::err_duplicate_attribute) << AL;
+ return;
+ }
+ if (!isValidAnalyseAsClassAttr(D, Str)) {
+ S.Diag(AL.getLoc(), diag::warn_attribute_type_not_supported) << AL;
+ return;
+ }
+
+ D->addAttr(::new (S.Context) AnalyseAsClassAttr(S.Context, AL, Str));
+}
+
static bool hasBTFDeclTagAttr(Decl *D, StringRef Tag) {
for (const auto *I : D->specific_attrs<BTFDeclTagAttr>()) {
if (I->getBTFDeclTag() == Tag)
@@ -7551,6 +7574,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
case ParsedAttr::AT_BPFPreserveStaticOffset:
handleSimpleAttribute<BPFPreserveStaticOffsetAttr>(S, D, AL);
break;
+ case ParsedAttr::AT_AnalyseAsClass:
+ handleAnalyseAsClass(S, D, AL);
+ break;
case ParsedAttr::AT_BTFDeclTag:
handleBTFDeclTagAttr(S, D, AL);
break;
>From fe028c7292be79ba6f753ebae1039e65aa0032e9 Mon Sep 17 00:00:00 2001
From: khickett <khicketts at bloomberg.net>
Date: Tue, 28 Apr 2026 15:43:52 +0100
Subject: [PATCH 2/5] analyse_as_class works ok
---
.../Models/UncheckedOptionalAccessModel.cpp | 4 +
hicketts_test/hicketts_optional.h | 83 +++++++++++++++++++
hicketts_test/test_hicketts_optional.cpp | 83 +++++++++++++++++++
3 files changed, 170 insertions(+)
create mode 100644 hicketts_test/hicketts_optional.h
create mode 100644 hicketts_test/test_hicketts_optional.cpp
diff --git a/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp b/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
index 568564fb361f4..317f5034b0145 100644
--- a/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
+++ b/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
@@ -12,6 +12,7 @@
//===----------------------------------------------------------------------===//
#include "clang/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.h"
+#include "clang/AST/Attr.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/Expr.h"
@@ -89,6 +90,9 @@ static bool hasOptionalClassName(const CXXRecordDecl &RD) {
isFullyQualifiedNamespaceEqualTo(*N, "bdlb", "BloombergLP");
}
+ if (RD.hasAttr<AnalyseAsClassAttr>())
+ return true;
+
return false;
}
diff --git a/hicketts_test/hicketts_optional.h b/hicketts_test/hicketts_optional.h
new file mode 100644
index 0000000000000..b915b01f70290
--- /dev/null
+++ b/hicketts_test/hicketts_optional.h
@@ -0,0 +1,83 @@
+#ifndef HICKETTS_OPTIONAL_H_
+#define HICKETTS_OPTIONAL_H_
+
+/// A custom optional-like type with differently named functions.
+/// Mirrors std::optional semantics but uses its own vocabulary
+/// In order to test implementation of attributes for clang-tidy
+namespace mylib {
+
+struct nothing_t {
+ constexpr explicit nothing_t() {}
+};
+
+constexpr nothing_t nothing;
+
+template <typename T>
+class [[clang::analyse_as_class("std::optional")]] HickettsOptional {
+ T *storage_ = nullptr;
+
+public:
+ constexpr HickettsOptional() noexcept {}
+
+ constexpr HickettsOptional(nothing_t) noexcept {}
+
+ HickettsOptional(const HickettsOptional &) = default;
+
+ HickettsOptional(HickettsOptional &&) = default;
+
+ // Equivalent to std::optional::value()
+ //[[clang_analyse_as_method(std::optional::value)]]
+ const T &unwrap() const & { return *storage_; }
+ T &unwrap() & { return *storage_; }
+ const T &&unwrap() const && { return static_cast<const T &&>(*storage_); }
+ T &&unwrap() && { return static_cast<T &&>(*storage_); }
+
+ const T &value() const & { return *storage_; }
+ T &value() & { return *storage_; }
+ const T &&value() const && { return static_cast<const T &&>(*storage_); }
+ T &&value() && { return static_cast<T &&>(*storage_); }
+
+ // Equivalent to std::optional::operator*()
+ const T &deref() const & { return *storage_; }
+ T &deref() & { return *storage_; }
+
+ // Equivalent to std::optional::operator->()
+ const T* operator ->() const { return storage_; }
+ T* operator ->() { return storage_; }
+ const T *arrow() const { return storage_; }
+ T *arrow() { return storage_; }
+
+ // Equivalent to std::optional::operator bool / has_value()
+ constexpr bool hasValue() const noexcept { return storage_ != nullptr; }
+ constexpr explicit operator bool() const noexcept { return storage_ != nullptr; }
+ constexpr bool isPresent() const noexcept { return storage_ != nullptr; }
+ constexpr bool isEmpty() const noexcept { return storage_ == nullptr; }
+
+ // Equivalent to std::optional::value_or()
+ template <typename U>
+ constexpr T unwrapOr(U &&fallback) const & {
+ return storage_ ? *storage_ : static_cast<T>(fallback);
+ }
+
+ // Equivalent to std::optional::emplace()
+ template <typename... Args>
+ T &construct(Args &&...args) { return *storage_; }
+
+ // Equivalent to std::optional::reset()
+ void clear() noexcept { storage_ = nullptr; }
+
+ // Equivalent to std::optional::swap()
+ void exchange(HickettsOptional &other) noexcept {
+ T *tmp = storage_;
+ storage_ = other.storage_;
+ other.storage_ = tmp;
+ }
+
+ // Assignment
+ template <typename U>
+ HickettsOptional &operator=(const U &u) { return *this; }
+};
+
+} // namespace mylib
+
+#endif // HICKETTS_OPTIONAL_H_
diff --git a/hicketts_test/test_hicketts_optional.cpp b/hicketts_test/test_hicketts_optional.cpp
new file mode 100644
index 0000000000000..fa0153a79f341
--- /dev/null
+++ b/hicketts_test/test_hicketts_optional.cpp
@@ -0,0 +1,83 @@
+// Test cases for mylib::HickettsOptional — a custom optional-like type
+// with differently named functions.
+//
+// Run from hicketts_test/ with:
+// clang-tidy -checks='bugprone-unchecked-optional-access' \
+// test_hicketts_optional.cpp -- -I . -Wno-undefined-inline
+
+#include "hicketts_optional.h"
+
+// --- Unchecked access (should warn if the checker recognises HickettsOptional) ---
+
+static void uncheckedUnwrap(mylib::HickettsOptional<int> &Val) {
+ Val.unwrap(); // unchecked access — may be empty
+}
+
+static void uncheckedValue(mylib::HickettsOptional<int> &Val) {
+ Val.value(); // unchecked access — may be empty
+}
+
+static void uncheckedDeref(mylib::HickettsOptional<int> &Val) {
+ Val.deref(); // unchecked access — may be empty
+}
+
+// --- Checked access (should NOT warn) ---
+
+static void checkedWithBool(mylib::HickettsOptional<int> &Val) {
+ if (Val) {
+ Val.unwrap(); // safe — checked via operator bool
+ }
+}
+
+static void checkedValueWithBool(mylib::HickettsOptional<int> &Val) {
+ if (Val.hasValue()) {
+ Val.value(); // safe — checked via operator bool
+ }
+}
+
+static void checkedWithIsPresent(mylib::HickettsOptional<int> &Val) {
+ if (Val.isPresent()) {
+ Val.unwrap(); // safe — checked via isPresent()
+ }
+}
+
+static void checkedWithIsEmpty(mylib::HickettsOptional<int> &Val) {
+ if (!Val.isEmpty()) {
+ Val.unwrap(); // safe — checked via !isEmpty()
+ }
+}
+
+// --- State changes ---
+
+static void safeAfterConstruct(mylib::HickettsOptional<int> &Val) {
+ Val.construct(42);
+ Val.unwrap(); // safe — just constructed a value
+}
+
+static void unsafeAfterClear(mylib::HickettsOptional<int> &Val) {
+ Val.construct(42);
+ Val.clear();
+ Val.unwrap(); // unsafe — value was cleared
+}
+
+static void unsafeAfterExchange(mylib::HickettsOptional<int> &A,
+ mylib::HickettsOptional<int> &B) {
+ if (A) {
+ A.exchange(B);
+ A.unwrap(); // unsafe — a's state is now unknown
+ }
+}
+
+// --- Guarded paths ---
+
+static void constructCoversEmptyBranch(mylib::HickettsOptional<int> &Val) {
+ if (Val.isEmpty()) {
+ Val.construct(99);
+ }
+ Val.unwrap(); // safe — either was present, or construct filled it
+}
+
+static void unwrapOrIsAlwaysSafe(mylib::HickettsOptional<int> &Val) {
+ int X = Val.unwrapOr(0); // safe — fallback provided
+ (void)X;
+}
>From ec4ea5a3de75bcc71b0140a11f24298d4681fdb0 Mon Sep 17 00:00:00 2001
From: khickett <khicketts at bloomberg.net>
Date: Tue, 28 Apr 2026 16:35:01 +0100
Subject: [PATCH 3/5] partially working for per method analysis
---
.../Models/UncheckedOptionalAccessModel.cpp | 19 +++++++++++--
clang/lib/Sema/SemaDeclAttr.cpp | 27 +++++++++++++++++++
hicketts_test/hicketts_optional.h | 17 ++++++------
3 files changed, 52 insertions(+), 11 deletions(-)
diff --git a/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp b/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
index 317f5034b0145..f2c9aa756943e 100644
--- a/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
+++ b/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
@@ -230,6 +230,20 @@ AST_MATCHER(CXXOperatorCallExpr, hasOptionalOperatorObjectType) {
return hasReceiverTypeDesugaringToOptional(Node.getArg(0));
}
+AST_MATCHER_P(NamedDecl, hasAnalyseAsMethodName, std::string, MethodName) {
+ if (const auto *MD = dyn_cast<CXXMethodDecl>(&Node)) {
+ if (const auto *Attr = MD->getAttr<AnalyseAsMethodAttr>()) {
+ StringRef AttrValue = Attr->getMethodName();
+ auto Pos = AttrValue.rfind("::");
+ StringRef AttrMethodName = (Pos != StringRef::npos)
+ ? AttrValue.substr(Pos + 2)
+ : AttrValue;
+ return AttrMethodName == MethodName;
+ }
+ }
+ return false;
+}
+
auto isOptionalMemberCallWithNameMatcher(
ast_matchers::internal::Matcher<NamedDecl> matcher,
const std::optional<StatementMatcher> &Ignorable = std::nullopt) {
@@ -982,8 +996,9 @@ ignorableOptional(const UncheckedOptionalAccessModelOptions &Options) {
StatementMatcher
valueCall(const std::optional<StatementMatcher> &IgnorableOptional) {
- return isOptionalMemberCallWithNameMatcher(hasName("value"),
- IgnorableOptional);
+ return isOptionalMemberCallWithNameMatcher(
+ anyOf(hasName("value"), hasAnalyseAsMethodName("value")),
+ IgnorableOptional);
}
StatementMatcher
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 243c66b474384..f999c307b7111 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -6485,6 +6485,30 @@ static void handleAnalyseAsClass(Sema &S, Decl *D, const ParsedAttr &AL) {
D->addAttr(::new (S.Context) AnalyseAsClassAttr(S.Context, AL, Str));
}
+// for now this only handles std::optional (POC)
+static bool isValidAnalyseAsMethodAttr(Decl *D, StringRef Tag) {
+ // no validation is done currently. if someone writes something with a nonsense name,
+ // it simply won't be validated but also no warning will be emitted
+ // would be nice to do something smarter in the real implementation
+ return true;
+}
+
+static void handleAnalyseAsMethod(Sema &S, Decl *D, const ParsedAttr &AL) {
+ StringRef Str;
+ if (!S.checkStringLiteralArgumentAttr(AL, 0, Str))
+ return;
+ if (D->hasAttr<AnalyseAsMethodAttr>()) {
+ S.Diag(AL.getLoc(), diag::err_duplicate_attribute) << AL;
+ return;
+ }
+ if (!isValidAnalyseAsMethodAttr(D, Str)) {
+ S.Diag(AL.getLoc(), diag::warn_attribute_type_not_supported) << AL;
+ return;
+ }
+
+ D->addAttr(::new (S.Context) AnalyseAsMethodAttr(S.Context, AL, Str));
+}
+
static bool hasBTFDeclTagAttr(Decl *D, StringRef Tag) {
for (const auto *I : D->specific_attrs<BTFDeclTagAttr>()) {
if (I->getBTFDeclTag() == Tag)
@@ -7577,6 +7601,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
case ParsedAttr::AT_AnalyseAsClass:
handleAnalyseAsClass(S, D, AL);
break;
+ case ParsedAttr::AT_AnalyseAsMethod:
+ handleAnalyseAsMethod(S, D, AL);
+ break;
case ParsedAttr::AT_BTFDeclTag:
handleBTFDeclTagAttr(S, D, AL);
break;
diff --git a/hicketts_test/hicketts_optional.h b/hicketts_test/hicketts_optional.h
index b915b01f70290..43ec98831a74b 100644
--- a/hicketts_test/hicketts_optional.h
+++ b/hicketts_test/hicketts_optional.h
@@ -26,11 +26,10 @@ class [[clang::analyse_as_class("std::optional")]] HickettsOptional {
HickettsOptional(HickettsOptional &&) = default;
// Equivalent to std::optional::value()
- //[[clang_analyse_as_method(std::optional::value)]]
- const T &unwrap() const & { return *storage_; }
- T &unwrap() & { return *storage_; }
- const T &&unwrap() const && { return static_cast<const T &&>(*storage_); }
- T &&unwrap() && { return static_cast<T &&>(*storage_); }
+ [[clang::analyse_as_method("std::optional::value")]] const T &unwrap() const & { return *storage_; }
+ [[clang::analyse_as_method("std::optional::value")]] T &unwrap() & { return *storage_; }
+ [[clang::analyse_as_method("std::optional::value")]] const T &&unwrap() const && { return static_cast<const T &&>(*storage_); }
+ [[clang::analyse_as_method("std::optional::value")]] T &&unwrap() && { return static_cast<T &&>(*storage_); }
const T &value() const & { return *storage_; }
T &value() & { return *storage_; }
@@ -38,8 +37,8 @@ class [[clang::analyse_as_class("std::optional")]] HickettsOptional {
T &&value() && { return static_cast<T &&>(*storage_); }
// Equivalent to std::optional::operator*()
- const T &deref() const & { return *storage_; }
- T &deref() & { return *storage_; }
+ [[clang::analyse_as_method("std::optional::operator*")]] const T &deref() const & { return *storage_; }
+ [[clang::analyse_as_method("std::optional::operator*")]] T &deref() & { return *storage_; }
// Equivalent to std::optional::operator->()
const T* operator ->() const { return storage_; }
@@ -47,10 +46,10 @@ class [[clang::analyse_as_class("std::optional")]] HickettsOptional {
const T *arrow() const { return storage_; }
T *arrow() { return storage_; }
- // Equivalent to std::optional::operator bool / has_value()
+ // Equivalent to std::optional::operator bool / hasValue()
constexpr bool hasValue() const noexcept { return storage_ != nullptr; }
constexpr explicit operator bool() const noexcept { return storage_ != nullptr; }
- constexpr bool isPresent() const noexcept { return storage_ != nullptr; }
+ [[clang::analyse_as_method("std::optional::hasValue")]] constexpr bool isPresent() const noexcept { return storage_ != nullptr; }
constexpr bool isEmpty() const noexcept { return storage_ == nullptr; }
// Equivalent to std::optional::value_or()
>From 9d6520a6cac0f322cd67ac5a1619a6b8fd0128cd Mon Sep 17 00:00:00 2001
From: khickett <khicketts at bloomberg.net>
Date: Tue, 28 Apr 2026 17:07:21 +0100
Subject: [PATCH 4/5] extend method matching
.
---
.../Models/UncheckedOptionalAccessModel.cpp | 11 ++++++-----
hicketts_test/hicketts_optional.h | 15 +++++++--------
hicketts_test/test_hicketts_optional.cpp | 10 +++++-----
3 files changed, 18 insertions(+), 18 deletions(-)
diff --git a/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp b/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
index f2c9aa756943e..137b44560c529 100644
--- a/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
+++ b/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
@@ -358,7 +358,7 @@ auto isValueOrStringEmptyCall() {
callee(cxxMethodDecl(hasName("empty"))),
onImplicitObjectArgument(ignoringImplicit(
cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))),
- callee(cxxMethodDecl(hasName("value_or"),
+ callee(cxxMethodDecl(anyOf(hasName("value_or"), hasAnalyseAsMethodName("value_or")),
ofClass(optionalClass()))),
hasArgument(0, stringLiteral(hasSize(0))))
.bind(ValueOrCallID))));
@@ -1071,7 +1071,8 @@ auto buildTransferMatchSwitch() {
// will also pass for other types
.CaseOfCFGStmt<CXXMemberCallExpr>(
isOptionalMemberCallWithNameMatcher(
- hasAnyName("has_value", "hasValue")),
+ anyOf(hasAnyName("has_value", "hasValue"),
+ hasAnalyseAsMethodName("has_value"))),
transferOptionalHasValueCall)
// optional::operator bool
@@ -1101,7 +1102,7 @@ auto buildTransferMatchSwitch() {
// optional::emplace
.CaseOfCFGStmt<CXXMemberCallExpr>(
- isOptionalMemberCallWithNameMatcher(hasName("emplace")),
+ isOptionalMemberCallWithNameMatcher(anyOf(hasName("emplace"), hasAnalyseAsMethodName("emplace"))),
[](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
LatticeTransferState &State) {
if (RecordStorageLocation *Loc =
@@ -1112,7 +1113,7 @@ auto buildTransferMatchSwitch() {
// optional::reset
.CaseOfCFGStmt<CXXMemberCallExpr>(
- isOptionalMemberCallWithNameMatcher(hasName("reset")),
+ isOptionalMemberCallWithNameMatcher(anyOf(hasName("reset"), hasAnalyseAsMethodName("reset"))),
[](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
LatticeTransferState &State) {
if (RecordStorageLocation *Loc =
@@ -1124,7 +1125,7 @@ auto buildTransferMatchSwitch() {
// optional::swap
.CaseOfCFGStmt<CXXMemberCallExpr>(
- isOptionalMemberCallWithNameMatcher(hasName("swap")),
+ isOptionalMemberCallWithNameMatcher(anyOf(hasName("swap"), hasAnalyseAsMethodName("swap"))),
transferSwapCall)
// std::swap
diff --git a/hicketts_test/hicketts_optional.h b/hicketts_test/hicketts_optional.h
index 43ec98831a74b..2c6978a7e2339 100644
--- a/hicketts_test/hicketts_optional.h
+++ b/hicketts_test/hicketts_optional.h
@@ -37,8 +37,8 @@ class [[clang::analyse_as_class("std::optional")]] HickettsOptional {
T &&value() && { return static_cast<T &&>(*storage_); }
// Equivalent to std::optional::operator*()
- [[clang::analyse_as_method("std::optional::operator*")]] const T &deref() const & { return *storage_; }
- [[clang::analyse_as_method("std::optional::operator*")]] T &deref() & { return *storage_; }
+ [[clang::analyse_as_method("std::optional::value")]] const T &deref() const & { return *storage_; }
+ [[clang::analyse_as_method("std::optional::value")]] T &deref() & { return *storage_; }
// Equivalent to std::optional::operator->()
const T* operator ->() const { return storage_; }
@@ -47,10 +47,9 @@ class [[clang::analyse_as_class("std::optional")]] HickettsOptional {
T *arrow() { return storage_; }
// Equivalent to std::optional::operator bool / hasValue()
- constexpr bool hasValue() const noexcept { return storage_ != nullptr; }
+ constexpr bool has_value() const noexcept { return storage_ != nullptr; }
constexpr explicit operator bool() const noexcept { return storage_ != nullptr; }
- [[clang::analyse_as_method("std::optional::hasValue")]] constexpr bool isPresent() const noexcept { return storage_ != nullptr; }
- constexpr bool isEmpty() const noexcept { return storage_ == nullptr; }
+ [[clang::analyse_as_method("std::optional::has_value")]] constexpr bool isPresent() const noexcept { return storage_ != nullptr; }
// Equivalent to std::optional::value_or()
template <typename U>
@@ -60,13 +59,13 @@ class [[clang::analyse_as_class("std::optional")]] HickettsOptional {
// Equivalent to std::optional::emplace()
template <typename... Args>
- T &construct(Args &&...args) { return *storage_; }
+ [[clang::analyse_as_method("std::optional::emplace")]] T &construct(Args &&...args) { return *storage_; }
// Equivalent to std::optional::reset()
- void clear() noexcept { storage_ = nullptr; }
+ [[clang::analyse_as_method("std::optional::reset")]] void clear() noexcept { storage_ = nullptr; }
// Equivalent to std::optional::swap()
- void exchange(HickettsOptional &other) noexcept {
+ [[clang::analyse_as_method("std::optional::swap")]] void exchange(HickettsOptional &other) noexcept {
T *tmp = storage_;
storage_ = other.storage_;
other.storage_ = tmp;
diff --git a/hicketts_test/test_hicketts_optional.cpp b/hicketts_test/test_hicketts_optional.cpp
index fa0153a79f341..1ff16d13fe174 100644
--- a/hicketts_test/test_hicketts_optional.cpp
+++ b/hicketts_test/test_hicketts_optional.cpp
@@ -30,7 +30,7 @@ static void checkedWithBool(mylib::HickettsOptional<int> &Val) {
}
static void checkedValueWithBool(mylib::HickettsOptional<int> &Val) {
- if (Val.hasValue()) {
+ if (Val.has_value()) {
Val.value(); // safe — checked via operator bool
}
}
@@ -41,11 +41,11 @@ static void checkedWithIsPresent(mylib::HickettsOptional<int> &Val) {
}
}
-static void checkedWithIsEmpty(mylib::HickettsOptional<int> &Val) {
+/* static void checkedWithIsEmpty(mylib::HickettsOptional<int> &Val) {
if (!Val.isEmpty()) {
Val.unwrap(); // safe — checked via !isEmpty()
}
-}
+} NYI */
// --- State changes ---
@@ -70,12 +70,12 @@ static void unsafeAfterExchange(mylib::HickettsOptional<int> &A,
// --- Guarded paths ---
-static void constructCoversEmptyBranch(mylib::HickettsOptional<int> &Val) {
+/*static void constructCoversEmptyBranch(mylib::HickettsOptional<int> &Val) {
if (Val.isEmpty()) {
Val.construct(99);
}
Val.unwrap(); // safe — either was present, or construct filled it
-}
+}*/
static void unwrapOrIsAlwaysSafe(mylib::HickettsOptional<int> &Val) {
int X = Val.unwrapOr(0); // safe — fallback provided
>From 34ce7b2b289e5e5a4cb0d708d34f4675d7bdc426 Mon Sep 17 00:00:00 2001
From: khickett <khicketts at bloomberg.net>
Date: Thu, 30 Apr 2026 12:48:57 +0100
Subject: [PATCH 5/5] missed a bit
---
clang/include/clang/Basic/Attr.td | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 97536ac7a1966..adc127df5756e 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -921,6 +921,22 @@ def AlignValue : Attr {
let Documentation = [AlignValueDocs];
}
+def AnalyseAsClass : InheritableAttr {
+ let Spellings = [Clang<"analyse_as_class">];
+ let Args = [StringArgument<"ClassName">];
+ let Subjects = SubjectList<[Record],
+ ErrorDiag>;
+ let Documentation = [Undocumented];
+}
+
+def AnalyseAsMethod : InheritableAttr {
+ let Spellings = [Clang<"analyse_as_method">];
+ let Args = [StringArgument<"MethodName">];
+ let Subjects = SubjectList<[CXXMethod],
+ ErrorDiag>;
+ let Documentation = [Undocumented];
+}
+
def AlignMac68k : InheritableAttr {
// This attribute has no spellings as it is only ever created implicitly.
let Spellings = [];
More information about the cfe-commits
mailing list