<table border="1" cellspacing="0" cellpadding="8">
    <tr>
        <th>Issue</th>
        <td>
            <a href=https://github.com/llvm/llvm-project/issues/146089>146089</a>
        </td>
    </tr>

    <tr>
        <th>Summary</th>
        <td>
            [clang-tidy] misc-unconventional-assign-operator false positive for classes satisfying std::indirectly_writable
        </td>
    </tr>

    <tr>
      <th>Labels</th>
      <td>
            clang-tidy,
            false-positive
      </td>
    </tr>

    <tr>
      <th>Assignees</th>
      <td>
      </td>
    </tr>

    <tr>
      <th>Reporter</th>
      <td>
          justusranvier
      </td>
    </tr>
</table>

<pre>
    The [std::indirectly_writable](https://en.cppreference.com/w/cpp/iterator/indirectly_writable.html) concept which is necessary to create proxy references for iterators that are usable in range algorithms requires const-qualified assignment operators.

Those operators currently do not meet clang-tidy's of "conventional", which predates C++20 and range algorithms.

>From cppreference:

> The required expressions with `const_cast` prevent [indirectly_readable](https://en.cppreference.com/w/cpp/iterator/indirectly_readable.html) objects with prvalue reference types from satisfying the syntactic requirements of `indirectly_writable` by accident, while permitting proxy references to continue to work as long as their constness is shallow. See [Ranges TS issue 381](https://github.com/ericniebler/stl2/issues/381).

An example of a proxy type with shallow constness which is currently flagged by clang-tidy:

```
struct Proxy {
    auto GetA() const -> A&
    {
 return soa_->vector_of_a_[index_];
    }
    auto GetB() const -> B&
 {
        return soa_->vector_of_b_[index_];
    }

    auto swap(Proxy& rhs) -> void
    {
        indirect_swap(rhs);
 std::ranges::swap(soa_, rhs.soa_);
        std::ranges::swap(index_, rhs.index_);
    }

    Proxy(SoA* soa, std::size_t index)
        : soa_(soa)
        , index_(index)
    {
    }
    Proxy(Proxy const& rhs) = default;
    Proxy(Proxy&& rhs) = default;
    auto operator=(Proxy const& rhs) -> Proxy&
    {
        indirect_assign(rhs);
        soa_ = rhs.soa_;
        index_ = rhs.index_;

        return *this;
    }
 auto operator=(Proxy&& rhs) -> Proxy&
    {
        swap(rhs);

 return *this;
    }
    // NOLINTBEGIN(misc-unconventional-assign-operator)
    auto operator=(Proxy const& rhs) const -> Proxy const&
    {
        indirect_assign(rhs);

 return *this;
    }
    auto operator=(Proxy&& rhs) const -> Proxy const&
    {
        indirect_swap(rhs);

        return *this;
    }
 // NOLINTEND(misc-unconventional-assign-operator)

    ~Proxy() = default;

private:
    SoA* soa_;
    std::size_t index_;

    auto indirect_assign(Proxy const& rhs) const noexcept -> void
    {
 GetA() = rhs.GetA();
        GetB() = rhs.GetB();
    }
    auto indirect_swap(Proxy& rhs) const noexcept -> void
    {
 std::ranges::swap(GetA(), rhs.GetA());
        std::ranges::swap(GetB(), rhs.GetB());
    }
    friend auto swap(Proxy& lhs, Proxy& rhs) noexcept -> void { lhs.swap(rhs); }
};
```

</pre>
<img width="1" height="1" alt="" src="http://email.email.llvm.org/o/eJysV12P4joS_TXmpQRKO3yEBx6gmR6NtOpd7fQ7cpyCeNbYGbtCN_twf_uVgyHhc3pGN2qJdFKuqnNOlV0R3quNQZyx0YKNlj1RU2nd7EftqfZOmJ1C18ttsZ-9lQhstPBUsHTO0rkyhXIoSe9X706RyDWy0ZLxrCSqfLDhL4y_oBnIqnK4RodG4kDaLeMv74y_yKpi_EUROkHWhdtrj4OStprxKUhrJFYE76WSJSgPBiV6L9weyIJ0KAihcvZjD6dYHtbWwTGAByoFgXAItQ--QRlwwmwQhN5Yp6jcenD4s1YOfQjoqf-zFlqtFRZwIGqLhsBW0eOAJXOWzN9K67F9CrJ2Dg3pPRQWjCXYIhJILcymT6rYMz7xYNfAOJfW7NCQskZoxjnjzxFh5bAQhB6eGV8wvuAJCFNc5RszeHF2C12aA_3NG5Z-gaBcxFUAflQOvVfWeHhXVAIbJw3WlRSe2DgJoUNOQeyOIg5F8c9qfPR40tjmP1BSTKtyO6FrbNUE2ldB0gDVC1J-vVdmA1Qi-L0hIUnJI8wg04HicXKrTscJ5HsQUqoCDUXWNUKFbquIgt-rWgplZg0pU2O4f7fufyA8aGs24ZdKVO5QNQa9DyXqS6G1fR_Ad2xa579BPA9v30F5XyOk2dMtNjeKyjqPJKJT0ijMNQb2PGkeSAzLPeMvwQOfxiKYG8APsa00BuQiIgisHRiN6XRyPDVTW7FrLTYbLAI9nYI9VdM4iX_J3JOrJcF_mihssmDJHABA1GThK9Kc8Sw2rifohzqcMz6OVtHeIdXOgLdiFSx2KMm6lV2vxOpQffixChSli9O65UWcxXWcRYzTJhWuu7Hyh7G64fy7qBjPGsiMj8GVPoRugu6sKs7BxetYf6u4-rAohjntpk1j-8N9NAyZhtJ0pR8c7qdtcuF6tDjiicuP_03voIuIsu92zvg8cBRWnvx79X9cETRegpM2BZY21qtDuhfv-DMcA2cXa1uKWkWPSRwqqtGzQzJLl1DgWtSaWhBnS4Lqv7BvVDxu1Cxd3o3WSHry-lDWw8FwKexRIitWTSonEdMLJ_jRGkS2GpPrymV8TqXyV81wD9Q5H79GdKM-Oz16L3ojdNi24PXf__r2-rb48vXbK-PZVnnZr033gOsfqOqfcj2Vw6d16XT5ucEfaPQ5cJ9j9w_zukP553Q_o_3L6_J3SI-e_jo20M2OYcm8cmonKI4TYUW7Q3RK-eZGcV7HDY3XejxS2Vj8aGa-O_tr55A5NlD76LzPOudEx3RxaXqh-qVMl_v-59J8tEt38o07dffJb2z3HTSto8UNRy3EtVNoitsHmw4An-ES8DXUADJYDy4r-Xi-TOKB2p0cesUsLabpVPRw9jQZPSXpMONJr5yNprlA-TTmcroW2SR9GmXjROZZOhoXWTKa9NSMJ3yUjPnkaZim6XSQrif5dCjFejjM8kk-YsMEt0Lpgda77cC6Ta-ZlWZPw3GSTXta5Kh9863DeXcgD5M343wttMd-Zb0itcPweLTsuVlw1s_rjWfDRCtPvnVPinTz8dTxNlrCJxoRmmBwDNZ8q0gtvEffnW8ffW_1aqdnD4bHkGX86VfOhum6Oz1GUnYz_ncAAAD__xOFUII">