llvm / llvm/llvm-project

[clang-tidy] Add check for monadic operations on `std::optional` / `std::expected`

Open
#189,669 1 comment 0 reactions 0 assignees View on GitHub
c++23 check-request clang-tidy
Dominant language
LLVM
Stars
40.5k
Forks
18.7k
PR merge metrics
PR metrics pending

Description

It would be useful to have a clang-tidy check that recognizes canonical manual control-flow over `std::optional` and `std::expected` and suggests the corresponding monadic operations.

Today code is often written in the older manual style with `if`, `has_value()`, `return std::nullopt`, or `return std::unexpected(...)`, even when the logic is really just spelling a monadic operation.

For example, code like this is common:

```cpp
std::optional f(const std::optional &opt) {
if (!opt)
return std::nullopt;
return g(*opt);
}
```

```cpp
std::expected f(std::expected exp) {
if (!exp)
return std::unexpected(exp.error());
return g(*exp);
}
```

```cpp
std::expected f(std::expected exp) {
if (exp)
return *exp;
return std::unexpected(convert_error(exp.error()));
}
```

but the clearer spelling is often:

```cpp
return opt.and_then(g);
return std::move(exp).and_then(g);
return std::move(exp).transform_error(convert_error);
```

Similarly, straightforward value-mapping code can often be written with `transform`, and fallback-on-empty / fallback-on-error code can often be written with `or_else`.

These forms express the intent directly: continue on success, transform the contained value, or handle the empty / error case, instead of spelling the same logic indirectly with manual branching and reconstruction.

Open question: should this be a `modernize` check or a `readability` check?

For `std::optional`, the monadic operations are newer than the type itself, so a `modernize` classification would make sense. For `std::expected`, however, the monadic operations are part of the facility from the start, so the motivation is arguably more about readability and direct expression of intent than modernization. It may be reasonable to treat the check as a readability-oriented one even if it also serves a modernization role for `std::optional`.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.