[llvm] add some test files for playing with (PR #192683)

via llvm-commits llvm-commits at lists.llvm.org
Fri Apr 17 08:16:41 PDT 2026


https://github.com/KHicketts created https://github.com/llvm/llvm-project/pull/192683

None

>From 3781e6fa8e233cf80e23eaeb6969462a860157cf Mon Sep 17 00:00:00 2001
From: Kay Hicketts <khicketts at bloomberg.net>
Date: Fri, 17 Apr 2026 14:30:14 +0000
Subject: [PATCH] add some test files for playing with

---
 hicketts_test/clang-tidy-overview.md     | 58 ++++++++++++++++++
 hicketts_test/hicketts_optional.h        | 75 ++++++++++++++++++++++++
 hicketts_test/run_instructions.md        | 19 ++++++
 hicketts_test/test_hicketts_optional.cpp | 73 +++++++++++++++++++++++
 hicketts_test/test_makevalue_fix.cpp     | 68 +++++++++++++++++++++
 5 files changed, 293 insertions(+)
 create mode 100644 hicketts_test/clang-tidy-overview.md
 create mode 100644 hicketts_test/hicketts_optional.h
 create mode 100644 hicketts_test/run_instructions.md
 create mode 100644 hicketts_test/test_hicketts_optional.cpp
 create mode 100644 hicketts_test/test_makevalue_fix.cpp

diff --git a/hicketts_test/clang-tidy-overview.md b/hicketts_test/clang-tidy-overview.md
new file mode 100644
index 0000000000000..8bb42792f16e2
--- /dev/null
+++ b/hicketts_test/clang-tidy-overview.md
@@ -0,0 +1,58 @@
+# How clang-tidy Works
+
+## Entry Point
+
+`clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp` — parses CLI args (`--checks`, `--fix`, etc.), builds an options provider, and calls `runClangTidy()`.
+
+## Module & Check Registration
+
+Uses LLVM's static registry pattern:
+
+1. **Modules** (e.g. `BugproneModule`, `ModernizeModule`) subclass `ClangTidyModule` and register themselves statically via `ClangTidyModuleRegistry::Add<T>`.
+2. Each module's `addCheckFactories()` registers individual checks by name:
+   ```cpp
+   Factories.registerCheck<UseNullptrCheck>("modernize-use-nullptr");
+   ```
+3. `ClangTidyForceLinker.h` has linker anchors ensuring all modules get linked in.
+
+## Per-TU Processing
+
+For each translation unit, `ClangTidyASTConsumerFactory::createASTConsumer()`:
+
+1. Loads merged options for the file (CLI + `.clang-tidy` hierarchy)
+2. Instantiates only the **enabled** checks
+3. Each check calls `registerMatchers(MatchFinder*)` to register AST matchers
+4. Optionally registers preprocessor callbacks (for `#include`/`#define` analysis)
+5. `MatchFinder` traverses the AST, calling `check(MatchResult&)` on matches
+
+## Check Lifecycle
+
+Every check extends `ClangTidyCheck` (`ClangTidyCheck.h`):
+
+- **`registerMatchers()`** — declare what AST patterns to match
+- **`check()`** — called on each match; emit diagnostics via `diag()` with optional `FixItHint`s
+- **`storeOptions()`** — serialize check-specific options (read via `Options.get()`)
+- **`isLanguageVersionSupported()`** — skip checks for wrong language modes
+
+## Configuration
+
+`ClangTidyOptions.h/cpp` handles `.clang-tidy` YAML files. Options merge with priority: defaults < parent directory configs < local config < CLI flags. Each option carries a `Priority` value to resolve conflicts.
+
+## Diagnostic Flow
+
+```
+check->diag(loc, "message")
+  -> ClangTidyContext::diag()
+    -> DiagnosticsEngine
+      -> ClangTidyDiagnosticConsumer
+        -> Creates ClangTidyError (SourceManager-independent)
+        -> Converts FixItHints to tooling::Replacement
+        -> Filters by header regex, NOLINT comments
+        -> Applies fixes if --fix was passed
+```
+
+## Key Design
+
+- **Pluggable**: new checks just subclass `ClangTidyCheck` and register in a module — no core changes needed
+- **AST Matcher-based**: most checks are declarative pattern matches over the Clang AST
+- **Hierarchical config**: `.clang-tidy` files cascade up the directory tree, merged by priority
diff --git a/hicketts_test/hicketts_optional.h b/hicketts_test/hicketts_optional.h
new file mode 100644
index 0000000000000..050fd2467a0d3
--- /dev/null
+++ b/hicketts_test/hicketts_optional.h
@@ -0,0 +1,75 @@
+#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_); }
+
+  // Equivalent to std::optional::operator*()
+  const T &deref() const & { return *storage_; }
+  T &deref() & { return *storage_; }
+
+  // Equivalent to std::optional::operator->()
+  const T *arrow() const { return storage_; }
+  T *arrow() { return storage_; }
+
+  // Equivalent to std::optional::operator bool / has_value()
+  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/run_instructions.md b/hicketts_test/run_instructions.md
new file mode 100644
index 0000000000000..8eea4dddd823a
--- /dev/null
+++ b/hicketts_test/run_instructions.md
@@ -0,0 +1,19 @@
+# Run Instructions
+
+## Test the PR #144313 fix (makeValue/makeValueInplace)
+```bash
+clang-tidy -checks='bugprone-unchecked-optional-access' \
+  test_makevalue_fix.cpp -- \
+  -I ../clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/unchecked-optional-access \
+  -Wno-undefined-inline
+```
+
+## Test HickettsOptional (custom optional-like type)
+```bash
+clang-tidy -checks='bugprone-unchecked-optional-access' \
+  test_hicketts_optional.cpp -- \
+  -I . \
+  -Wno-undefined-inline
+```
+
+All commands assume you are running from the `hicketts_test/` directory.
diff --git a/hicketts_test/test_hicketts_optional.cpp b/hicketts_test/test_hicketts_optional.cpp
new file mode 100644
index 0000000000000..9cf229440ddcf
--- /dev/null
+++ b/hicketts_test/test_hicketts_optional.cpp
@@ -0,0 +1,73 @@
+// 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) ---
+
+void unchecked_unwrap(mylib::HickettsOptional<int> &val) {
+  val.unwrap(); // unchecked access — may be empty
+}
+
+void unchecked_deref(mylib::HickettsOptional<int> &val) {
+  val.deref(); // unchecked access — may be empty
+}
+
+// --- Checked access (should NOT warn) ---
+
+void checked_with_bool(mylib::HickettsOptional<int> &val) {
+  if (val) {
+    val.unwrap(); // safe — checked via operator bool
+  }
+}
+
+void checked_with_isPresent(mylib::HickettsOptional<int> &val) {
+  if (val.isPresent()) {
+    val.unwrap(); // safe — checked via isPresent()
+  }
+}
+
+void checked_with_isEmpty(mylib::HickettsOptional<int> &val) {
+  if (!val.isEmpty()) {
+    val.unwrap(); // safe — checked via !isEmpty()
+  }
+}
+
+// --- State changes ---
+
+void safe_after_construct(mylib::HickettsOptional<int> &val) {
+  val.construct(42);
+  val.unwrap(); // safe — just constructed a value
+}
+
+void unsafe_after_clear(mylib::HickettsOptional<int> &val) {
+  val.construct(42);
+  val.clear();
+  val.unwrap(); // unsafe — value was cleared
+}
+
+void unsafe_after_exchange(mylib::HickettsOptional<int> &a,
+                           mylib::HickettsOptional<int> &b) {
+  if (a) {
+    a.exchange(b);
+    a.unwrap(); // unsafe — a's state is now unknown
+  }
+}
+
+// --- Guarded paths ---
+
+void construct_covers_empty_branch(mylib::HickettsOptional<int> &val) {
+  if (val.isEmpty()) {
+    val.construct(99);
+  }
+  val.unwrap(); // safe — either was present, or construct filled it
+}
+
+void unwrapOr_is_always_safe(mylib::HickettsOptional<int> &val) {
+  int x = val.unwrapOr(0); // safe — fallback provided
+  (void)x;
+}
diff --git a/hicketts_test/test_makevalue_fix.cpp b/hicketts_test/test_makevalue_fix.cpp
new file mode 100644
index 0000000000000..d12129f52baf6
--- /dev/null
+++ b/hicketts_test/test_makevalue_fix.cpp
@@ -0,0 +1,68 @@
+// Test cases for the fix in llvm/llvm-project#144313
+//
+// This exercises the bugprone-unchecked-optional-access check's handling
+// of BloombergLP::bdlb::NullableValue::makeValue and makeValueInplace.
+//
+// Before the fix, cases marked "OK" below would produce false-positive
+// warnings because the checker didn't know makeValue/makeValueInplace
+// establish a valid value.
+//
+// Run with:
+//   clang-tidy -checks='bugprone-unchecked-optional-access' \
+//     test_makevalue_fix.cpp -- \
+//     -I <llvm-project>/clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/unchecked-optional-access
+
+#include "bde/types/bdlb_nullablevalue.h"
+
+// -- SHOULD NOT WARN (the fix) --
+
+// makeValue(val) on null branch guarantees value is present on all paths.
+void makeValue_covers_null_branch(BloombergLP::bdlb::NullableValue<int> &opt) {
+  if (opt.isNull()) {
+    opt.makeValue(42);
+  }
+  opt.value(); // OK — either was non-null already, or makeValue filled it
+}
+
+// makeValueInplace does the same thing via in-place construction.
+void makeValueInplace_covers_null_branch(BloombergLP::bdlb::NullableValue<int> &opt) {
+  if (opt.isNull()) {
+    opt.makeValueInplace(42);
+  }
+  opt.value(); // OK
+}
+
+// Unconditional makeValue — always safe to access afterwards.
+void unconditional_makeValue(BloombergLP::bdlb::NullableValue<int> &opt) {
+  opt.makeValue(100);
+  opt.value(); // OK
+}
+
+// Zero-arg makeValue — default-constructs the value.
+void makeValue_no_args(BloombergLP::bdlb::NullableValue<int> &opt) {
+  opt.makeValue();
+  opt.value(); // OK
+}
+
+// -- SHOULD WARN (not fixed by makeValue) --
+
+// Accessing without any check or makeValue is still unsafe.
+void no_check_no_makeValue(BloombergLP::bdlb::NullableValue<int> &opt) {
+  opt.value(); // WARNING: unchecked access to optional value
+}
+
+// reset() after makeValue invalidates the value.
+void makeValue_then_reset(BloombergLP::bdlb::NullableValue<int> &opt) {
+  opt.makeValue(42);
+  opt.reset();
+  opt.value(); // WARNING: value was reset
+}
+
+// makeValue on a *different* object doesn't help.
+void makeValue_wrong_object(BloombergLP::bdlb::NullableValue<int> &a,
+                            BloombergLP::bdlb::NullableValue<int> &b) {
+  if (a.isNull()) {
+    b.makeValue(42);
+  }
+  a.value(); // WARNING: a may still be null
+}



More information about the llvm-commits mailing list