llvm / llvm/llvm-project

`cplusplus.Move` treats the left-hand side of an explicit-object move assignment as moved-from

Open
#218,227 3 comments 0 reactions 0 assignees View on GitHub
clang:static analyzer false-positive
Dominant language
LLVM
Stars
40.5k
Forks
18.7k
PR merge metrics
PR metrics pending

Description

## Description

The Clang Static Analyzer's `cplusplus.Move` checker appears to model a C++23 explicit-object move-assignment operator incorrectly.

For an operator declared as:

```cpp
auto operator=(this Owner& self, Owner&& other) noexcept -> Owner&;
```

the analyzer marks the explicit object argument (`self`, the left-hand side of the assignment) as moved-from after an expression such as:

```cpp
target = std::move(source);
```

It should mark `source` as moved-from. `target` has just received a new value and remains valid.

This produces false positives when `target` is subsequently moved. It also makes `scan-build --status-bugs` fail otherwise successful builds.

## Minimal reproducer

[godbolt link](https://godbolt.org/z/q9GG4jbqT)

```cpp
#include

struct Owner {
int* pointer{};

constexpr Owner() noexcept = default;

constexpr Owner(Owner&& other) noexcept
: pointer{std::exchange(other.pointer, nullptr)} {
}

constexpr auto operator=(this Owner& self, Owner&& other) noexcept -> Owner& {
self.pointer = std::exchange(other.pointer, nullptr);
return self;
}
};

struct Holder {
Owner value;

constexpr explicit Holder(Owner&& source) noexcept
: value{std::move(source)} {
}
};

void reproducer() {
Owner target{};
Owner source{};
target = std::move(source);
Holder holder{std::move(target)};
}
```

## Command

```sh
clang++ --analyze \
-Xanalyzer -analyzer-checker=cplusplus.Move \
-Xanalyzer -analyzer-output=text \
-std=c++23 repro.cc
```

The same issue is visible when running a CMake build through `scan-build --status-bugs`.

## Actual result

The analyzer reports that `target` is moved-from after the move-assignment expression and warns when it is subsequently moved into `Holder`:

```text
warning: Moved-from object 'target' is moved [cplusplus.Move]
note: Object 'target' is moved
target = std::move(source);
^~~~~~~~~~~~~~~~~~~~~~~~~~
note: Calling constructor for 'Holder'
Holder holder{std::move(target)};
^~~~~~~~~~~~~~~~~~~~~~~~~
note: Moved-from object 'target' is moved
: value{std::move(source)} {
^~~~~~~~~~~~~~~~~~~~~~~~
```

## Expected result

No diagnostic should be emitted for the later move from `target`.

After `target = std::move(source)`, `source` is the moved-from object. The move-assignment operator has assigned a new valid state to `target`.

## Comparison with an implicit-object move-assignment operator

Replacing only the move-assignment operator with the traditional implicit-object form removes the diagnostic:

```cpp
constexpr auto operator=(Owner&& other) noexcept -> Owner& {
pointer = std::exchange(other.pointer, nullptr);
return *this;
}
```

This suggests that the problem is specifically in the analyzer's modeling of the explicit object parameter rather than in its general modeling of move assignment.

## Versions observed

- Clang 21.1.8, installed from Ubuntu 26.04 packages and invoked through `scan-build`, reports the false positive in the original project.
- Clang 23.0.0git (`4c4c1db7c69a6fda6cfa6bc6066bb09a433edc89`), invoked directly with `clang++ --analyze` on Windows, reproduces it with the minimal example above.

The implicit-object variant produces no warning under the same Clang 23 analyzer invocation.

Contributor guide

Open the contributing guide

Research direction

Start with the minimal reproducer in repro.cc and run the shown clang++ --analyze command with the cplusplus.Move checker. Trace how the checker models explicit-object move assignment, then add regression coverage showing that source is moved-from while target remains valid and that no later diagnostic is emitted.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
compilers
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
74/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.