Repository: llvm/llvm-project
Pull request #132242: [clang-tidy] Add check bugprone-misleading-setter-of-reference
Author: login: balazske
Labels: name: clang-tools-extra
name: clang-tidy
Created: 2025-03-20T15:50:06Z | Merged: 2025-05-17T08:26:14Z

There can be concerns about the usefulness of this check but for some code it can be useful.


Diff:
diff --git a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
index b780a85bdf3fe..64f4a524daf0d 100644
--- a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
@@ -41,6 +41,7 @@
 #include "LambdaFunctionNameCheck.h"
 #include "MacroParenthesesCheck.h"
 #include "MacroRepeatedSideEffectsCheck.h"
+#include "MisleadingSetterOfReferenceCheck.h"
 #include "MisplacedOperatorInStrlenInAllocCheck.h"
 #include "MisplacedPointerArithmeticInAllocCheck.h"
 #include "MisplacedWideningCastCheck.h"
@@ -170,6 +171,8 @@ class BugproneModule : public ClangTidyModule {
         "bugprone-macro-parentheses");
     CheckFactories.registerCheck<MacroRepeatedSideEffectsCheck>(
         "bugprone-macro-repeated-side-effects");
+    CheckFactories.registerCheck<MisleadingSetterOfReferenceCheck>(
+        "bugprone-misleading-setter-of-reference");
     CheckFactories.registerCheck<MisplacedOperatorInStrlenInAllocCheck>(
         "bugprone-misplaced-operator-in-strlen-in-alloc");
     CheckFactories.registerCheck<MisplacedPointerArithmeticInAllocCheck>(
diff --git a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
index e310ea9c94543..d862794cde323 100644
--- a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
@@ -42,6 +42,7 @@ add_clang_library(clangTidyBugproneModule STATIC
   LambdaFunctionNameCheck.cpp
   MacroParenthesesCheck.cpp
   MacroRepeatedSideEffectsCheck.cpp
+  MisleadingSetterOfReferenceCheck.cpp
   MisplacedOperatorInStrlenInAllocCheck.cpp
   MisplacedPointerArithmeticInAllocCheck.cpp
   MisplacedWideningCastCheck.cpp
diff --git a/clang-tools-extra/clang-tidy/bugprone/MisleadingSetterOfReferenceCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/MisleadingSetterOfReferenceCheck.cpp
new file mode 100644
index 0000000000000..4aba5831e6772
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/MisleadingSetterOfReferenceCheck.cpp
@@ -0,0 +1,61 @@
+//===--- MisleadingSetterOfReferenceCheck.cpp - clang-tidy-----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MisleadingSetterOfReferenceCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+void MisleadingSetterOfReferenceCheck::registerMatchers(MatchFinder *Finder) {
+  auto RefField = fieldDecl(hasType(hasCanonicalType(referenceType(
+                                pointee(equalsBoundNode("type"))))))
+                      .bind("member");
+  auto AssignLHS = memberExpr(
+      hasObjectExpression(ignoringParenCasts(cxxThisExpr())), member(RefField));
+  auto DerefOperand = expr(ignoringParenCasts(
+      declRefExpr(to(parmVarDecl(equalsBoundNode("parm"))))));
+  auto AssignRHS = expr(ignoringParenCasts(
+      unaryOperator(hasOperatorName("*"), hasUnaryOperand(DerefOperand))));
+
+  auto BinaryOpAssign = binaryOperator(hasOperatorName("="), hasLHS(AssignLHS),
+                                       hasRHS(AssignRHS));
+  auto CXXOperatorCallAssign = cxxOperatorCallExpr(
+      hasOverloadedOperatorName("="), hasLHS(AssignLHS), hasRHS(AssignRHS));
+
+  auto SetBody =
+      compoundStmt(statementCountIs(1),
+                   anyOf(has(BinaryOpAssign), has(CXXOperatorCallAssign)));
+  auto BadSetFunction =
+      cxxMethodDecl(
+          parameterCountIs(1),
+          hasParameter(
+              0,
+              parmVarDecl(hasType(hasCanonicalType(pointerType(pointee(qualType(
+                              hasCanonicalType(qualType().bind("type"))))))))
+                  .bind("parm")),
+          hasBody(SetBody))
+          .bind("bad-set-function");
+  Finder->addMatcher(BadSetFunction, this);
+}
+
+void MisleadingSetterOfReferenceCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  const auto *Found = Result.Nodes.getNodeAs<CXXMethodDecl>("bad-set-function");
+  const auto *Member = Result.Nodes.getNodeAs<FieldDecl>("member");
+
+  diag(Found->getBeginLoc(),
+       "function '%0' can be mistakenly used in order to change the "
+       "reference '%1' instead of the value of it; consider not using a "
+       "pointer as argument")
+      << Found->getName() << Member->getName();
+}
+
+} // namespace clang::tidy::bugprone
diff --git a/clang-tools-extra/clang-tidy/bugprone/MisleadingSetterOfReferenceCheck.h b/clang-tools-extra/clang-tidy/bugprone/MisleadingSetterOfReferenceCheck.h
new file mode 100644
index 0000000000000..99e7a9435cfa9
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/MisleadingSetterOfReferenceCheck.h
@@ -0,0 +1,37 @@
+//===--- MisleadingSetterOfReferenceCheck.h - clang-tidy---------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_MISLEADINGSETTEROFREFERENCECHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_MISLEADINGSETTEROFREFERENCECHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::bugprone {
+
+/// Emits a warning when a setter-like function that has a pointer parameter
+/// is used to set value of a field with reference type.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone/throw-keyword-missing.html
+class MisleadingSetterOfReferenceCheck : public ClangTidyCheck {
+public:
+  MisleadingSetterOfReferenceCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+    return LangOpts.CPlusPlus;
+  }
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+  std::optional<TraversalKind> getCheckTraversalKind() const override {
+    return TK_IgnoreUnlessSpelledInSource;
+  }
+};
+
+} // namespace clang::tidy::bugprone
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_MISLEADINGSETTEROFREFERENCECHECK_H
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 72aa05eb4dcd1..0b57f3bf9aff4 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -109,6 +109,12 @@ New checks
   Finds lambda captures that capture the ``this`` pointer and store it as class
   members without handle the copy and move constructors and the assignments.
 
+- New :doc:`bugprone-misleading-setter-of-reference
+  <clang-tidy/checks/bugprone/misleading-setter-of-reference>` check.
+
+  Finds setter-like member functions that take a pointer parameter and set a
+  reference member of the same class with the pointed value.
+
 - New :doc:`bugprone-unintended-char-ostream-output
   <clang-tidy/checks/bugprone/unintended-char-ostream-output>` check.
 
diff --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone/misleading-setter-of-reference.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone/misleading-setter-of-reference.rst
new file mode 100644
index 0000000000000..43da9ae0ad199
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone/misleading-setter-of-reference.rst
@@ -0,0 +1,46 @@
+.. title:: clang-tidy - bugprone-misleading-setter-of-reference
+
+bugprone-misleading-setter-of-reference
+=======================================
+
+Finds setter-like member functions that take a pointer parameter and set a
+reference member of the same class with the pointed value.
+
+The check detects member functions that take a single pointer parameter,
+and contain a single expression statement that dereferences the parameter and
+assigns the result to a data member with a reference type.
+
+The fact that a setter function takes a pointer might cause the belief that an
+internal reference (if it would be a pointer) is changed instead of the
+pointed-to (or referenced) value.
+
+Example:
+
+.. code-block:: c++
+
+  class MyClass {
+    int &InternalRef;  // non-const reference member
+  public:
+    MyClass(int &Value) : InternalRef(Value) {}
+
+    // Warning: This setter could lead to unintended behaviour.
+    void setRef(int *Value) {
+      InternalRef = *Value;  // This assigns to the referenced value, not changing what InternalRef references.
+    }
+  };
+
+  int main() {
+    int Value1 = 42;
+    int Value2 = 100;
+    MyClass X(Value1);
+
+    // This might look like it changes what InternalRef references to,
+    // but it actually modifies Value1 to be 100.
+    X.setRef(&Value2);
+  }
+
+Possible fixes:
+  - Change the parameter type of the "set" function to non-pointer type (for
+    example, a const reference).
+  - Change the type of the member variable to a pointer and in the "set"
+    function assign a value to the pointer (without dereference).
diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index 18f1467285fab..1ec476eef3420 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -109,6 +109,7 @@ Clang-Tidy Checks
    :doc:`bugprone-lambda-function-name <bugprone/lambda-function-name>`,
    :doc:`bugprone-macro-parentheses <bugprone/macro-parentheses>`, "Yes"
    :doc:`bugprone-macro-repeated-side-effects <bugprone/macro-repeated-side-effects>`,
+   :doc:`bugprone-misleading-setter-of-reference <bugprone/misleading-setter-of-reference>`,
    :doc:`bugprone-misplaced-operator-in-strlen-in-alloc <bugprone/misplaced-operator-in-strlen-in-alloc>`, "Yes"
    :doc:`bugprone-misplaced-pointer-arithmetic-in-alloc <bugprone/misplaced-pointer-arithmetic-in-alloc>`, "Yes"
    :doc:`bugprone-misplaced-widening-cast <bugprone/misplaced-widening-cast>`,
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/misleading-setter-of-reference.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/misleading-setter-of-reference.cpp
new file mode 100644
index 0000000000000..695e250528536
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/misleading-setter-of-reference.cpp
@@ -0,0 +1,121 @@
+// RUN: %check_clang_tidy %s bugprone-misleading-setter-of-reference %t
+
+struct X {
+  X &operator=(const X &) { return *this; }
+private:
+  int &Mem;
+  friend class Test1;
+};
+
+class Test1 {
+  X &MemX;
+  int &MemI;
+protected:
+  long &MemL;
+public:
+  long &MemLPub;
+
+  Test1(X &MemX, int &MemI, long &MemL) : MemX(MemX), MemI(MemI), MemL(MemL), MemLPub(MemL) {}
+  void setI(int *NewValue) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: function 'setI' can be mistakenly used in order to change the reference 'MemI' instead of the value of it
+    MemI = *NewValue;
+  }
+  void setL(long *NewValue) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: function 'setL' can be mistakenly used in order to change the reference 'MemL' instead of the value of it
+    MemL = *NewValue;
+  }
+  void setX(X *NewValue) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: function 'setX' can be mistakenly used in order to change the reference 'MemX' instead of the value of it
+    MemX = *NewValue;
+  }
+  void set1(int *NewValue) {
+    MemX.Mem = *NewValue;
+  }
+  void set2(int *NewValue) {
+    MemL = static_cast<long>(*NewValue);
+  }
+  void set3(int *NewValue) {
+    MemI = *NewValue;
+    MemL = static_cast<long>(*NewValue);
+  }
+  void set4(long *NewValue, int) {
+    MemL = *NewValue;
+  }
+  void setLPub(long *NewValue) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: function 'setLPub' can be mistakenly used in order to change the reference 'MemLPub' instead of the value of it
+    MemLPub = *NewValue;
+  }
+
+private:
+  void set5(long *NewValue) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: function 'set5' can be mistakenly used in order to change the reference 'MemL' instead of the value of it
+    MemL = *NewValue;
+  }
+};
+
+class Base {
+protected:
+  int &MemI;
+};
+
+class Derived : public Base {
+public:
+  void setI(int *NewValue) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: function 'setI' can be mistakenly used in order to change the reference 'MemI' instead of the value of it
+    MemI = *NewValue;
+  }
+};
+
+using UIntRef = unsigned int &;
+using UIntPtr = unsigned int *;
+using UInt = unsigned int;
+
+class AliasTest {
+  UIntRef Value1;
+  UInt &Value2;
+  unsigned int &Value3;
+public:
+  void setValue1(UIntPtr NewValue) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: function 'setValue1' can be mistakenly used in order to change the reference 'Value1' instead of the value of it
+    Value1 = *NewValue;
+  }
+  void setValue2(unsigned int *NewValue) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: function 'setValue2' can be mistakenly used in order to change the reference 'Value2' instead of the value of it
+    Value2 = *NewValue;
+  }
+  void setValue3(UInt *NewValue) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: function 'setValue3' can be mistakenly used in order to change the reference 'Value3' instead of the value of it
+    Value3 = *NewValue;
+  }
+};
+
+template <typename T>
+class TemplateTest {
+  T &Mem;
+public:
+  TemplateTest(T &V) : Mem{V} {}
+  void setValue(T *NewValue) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: function 'setValue' can be mistakenly used in order to change the reference 'Mem' instead of the value of it
+    Mem = *NewValue;
+  }
+};
+
+void f_TemplateTest(char *Value) {
+  char CharValue;
+  TemplateTest<char> TTChar{CharValue};
+  TTChar.setValue(Value);
+}
+
+template <typename T>
+class AddMember {
+protected:
+  T &Value;
+};
+
+class TemplateBaseTest : public AddMember<int> {
+public:
+  void setValue(int *NewValue) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: function 'setValue' can be mistakenly used in order to change the reference 'Value' instead of the value of it
+    Value = *NewValue;
+  }
+};


Reviews:
user: login: NagyDonat
body: Overall looks good to me; see inline comments for minor inline suggestions.
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-25T13:14:43Z
user: login: vbvictor
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-01T09:02:18Z
user: login: balazske
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-01T10:18:05Z
user: login: balazske
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-01T10:20:04Z
user: login: NagyDonat
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-24T14:42:03Z
user: login: balazske
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-06T10:29:00Z
user: login: balazske
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-06T10:55:55Z
user: login: NagyDonat
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-06T13:18:32Z
user: login: NagyDonat
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-06T13:56:31Z
user: login: vbvictor
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-06T14:04:04Z
user: login: NagyDonat
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-06T14:15:11Z
user: login: EugeneZelenko
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-06T14:24:00Z
user: login: vbvictor
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-07T09:03:26Z
user: login: balazske
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-07T10:17:13Z
user: login: NagyDonat
body: LGTM, thanks for the updates!
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-07T10:39:21Z

Review comments:
diff_hunk: @@ -0,0 +1,58 @@
+//===--- MisleadingSetterOfReferenceCheck.cpp - clang-tidy-----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MisleadingSetterOfReferenceCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+void MisleadingSetterOfReferenceCheck::registerMatchers(MatchFinder *Finder) {
+  auto RefField =
+      fieldDecl(unless(isPublic()),
path: clang-tools-extra/clang-tidy/bugprone/MisleadingSetterOfReferenceCheck.cpp
user: login: NagyDonat
body: Is the `unless(isPublic())` condition necessary here?

At first I wanted to suggest that you should refer to it in the documentation (it's currently undocumented), but as I thought about that I think that it is a minor embellishment which is only loosely related to the core goals of the checker and just increases the complexity of the (already complex) matchers without providing real benefits.

(However, I can also accept keeping it if you think that it's important. In that case please refer to it in the docs.)
created_at: 2025-03-25T10:42:35Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,58 @@
+//===--- MisleadingSetterOfReferenceCheck.cpp - clang-tidy-----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MisleadingSetterOfReferenceCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+void MisleadingSetterOfReferenceCheck::registerMatchers(MatchFinder *Finder) {
+  auto RefField =
+      fieldDecl(unless(isPublic()),
+                hasType(referenceType(pointee(equalsBoundNode("type")))))
+          .bind("member");
+  auto AssignLHS =
+      memberExpr(hasObjectExpression(cxxThisExpr()), member(RefField));
+  auto DerefOperand = expr(ignoringParenImpCasts(
+      declRefExpr(to(parmVarDecl(equalsBoundNode("parm"))))));
+  auto AssignRHS = expr(ignoringParenImpCasts(
+      unaryOperator(hasOperatorName("*"), hasUnaryOperand(DerefOperand))));
+
+  auto BinaryOpAssign = binaryOperator(hasOperatorName("="), hasLHS(AssignLHS),
+                                       hasRHS(AssignRHS));
+  auto CXXOperatorCallAssign = cxxOperatorCallExpr(
+      hasOverloadedOperatorName("="), hasLHS(AssignLHS), hasRHS(AssignRHS));
+
+  auto SetBody =
+      compoundStmt(statementCountIs(1),
+                   anyOf(has(BinaryOpAssign), has(CXXOperatorCallAssign)));
+  auto BadSetFunction =
+      cxxMethodDecl(parameterCountIs(1), isPublic(),
+                    hasAnyParameter(parmVarDecl(hasType(pointerType(pointee(
+                                                    qualType().bind("type")))))
+                                        .bind("parm")),
+                    hasBody(SetBody))
+          .bind("bad-set-function");
+  Finder->addMatcher(BadSetFunction, this);
+}
+
+void MisleadingSetterOfReferenceCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  const auto *Found = Result.Nodes.getNodeAs<CXXMethodDecl>("bad-set-function");
+  const auto *Member = Result.Nodes.getNodeAs<FieldDecl>("member");
+
+  diag(Found->getBeginLoc(),
+       "function \'%0\' can be mistakenly used in order to change the "
+       "reference \'%1\' instead of the value of it")
path: clang-tools-extra/clang-tidy/bugprone/MisleadingSetterOfReferenceCheck.cpp
user: login: NagyDonat
body: ```suggestion
       "function '%0' can be mistakenly used in order to change the "
       "reference '%1' instead of the value of it")
```
There is no reason to escape those single quotes in a string.

created_at: 2025-03-25T12:47:58Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,42 @@
+.. title:: clang-tidy - bugprone-misleading-setter-of-reference
+
+bugprone-misleading-setter-of-reference
+=======================================
+
+Finds setter-like functions that take a pointer parameter and set a (non-const)
+reference with the pointed value. The fact that a setter function takes a
+pointer might cause the belief that an internal reference (if it would be a
+pointer) is changed instead of the pointed-to (or referenced) value.
+
+Only member functions are detected which have a single parameter and contain a
+single (maybe overloaded) assignment operator call.
+
+Example:
+
+.. code-block:: c++
+
+  class Widget {
+    int& ref_;  // non-const reference member
+  public:
+    // non-copyable
+    Widget(const Widget&) = delete;
+    // non-movable
+    Widget(Widget&&) = delete;
+ 
path: clang-tools-extra/docs/clang-tidy/checks/bugprone/misleading-setter-of-reference.rst
user: login: NagyDonat
body: ```suggestion
```

I understand that a class with a reference member is problematic if it doesn't disable the copy/move constructors, but there is no reason to explicitly spell out these deleted methods in this example, because they don't have a role in the problem that's reported by this checker.

If you want, you could add `// ...` at the end of the class definition to show that there are other methods (including these deleted ones) -- but I don't think that this is necessary.
created_at: 2025-03-25T13:12:09Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,50 @@
+// RUN: %check_clang_tidy %s bugprone-misleading-setter-of-reference %t
path: clang-tools-extra/test/clang-tidy/checkers/bugprone/misleading-setter-of-reference.cpp
user: login: vbvictor
body: Consider adding tests for inheritance. E.g. `Base` class has method `SetX` and `Derived` calls it.
created_at: 2025-04-01T07:45:30Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,50 @@
+// RUN: %check_clang_tidy %s bugprone-misleading-setter-of-reference %t
path: clang-tools-extra/test/clang-tidy/checkers/bugprone/misleading-setter-of-reference.cpp
user: login: vbvictor
body: Also, a test with templated class and templated method could be useful.
created_at: 2025-04-01T07:47:28Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,58 @@
+//===--- MisleadingSetterOfReferenceCheck.cpp - clang-tidy-----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MisleadingSetterOfReferenceCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+void MisleadingSetterOfReferenceCheck::registerMatchers(MatchFinder *Finder) {
+  auto RefField =
+      fieldDecl(unless(isPublic()),
+                hasType(referenceType(pointee(equalsBoundNode("type")))))
+          .bind("member");
+  auto AssignLHS =
+      memberExpr(hasObjectExpression(cxxThisExpr()), member(RefField));
+  auto DerefOperand = expr(ignoringParenImpCasts(
+      declRefExpr(to(parmVarDecl(equalsBoundNode("parm"))))));
+  auto AssignRHS = expr(ignoringParenImpCasts(
+      unaryOperator(hasOperatorName("*"), hasUnaryOperand(DerefOperand))));
+
+  auto BinaryOpAssign = binaryOperator(hasOperatorName("="), hasLHS(AssignLHS),
+                                       hasRHS(AssignRHS));
+  auto CXXOperatorCallAssign = cxxOperatorCallExpr(
+      hasOverloadedOperatorName("="), hasLHS(AssignLHS), hasRHS(AssignRHS));
+
+  auto SetBody =
+      compoundStmt(statementCountIs(1),
+                   anyOf(has(BinaryOpAssign), has(CXXOperatorCallAssign)));
+  auto BadSetFunction =
+      cxxMethodDecl(parameterCountIs(1), isPublic(),
+                    hasAnyParameter(parmVarDecl(hasType(pointerType(pointee(
+                                                    qualType().bind("type")))))
+                                        .bind("parm")),
+                    hasBody(SetBody))
+          .bind("bad-set-function");
+  Finder->addMatcher(BadSetFunction, this);
+}
+
+void MisleadingSetterOfReferenceCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  const auto *Found = Result.Nodes.getNodeAs<CXXMethodDecl>("bad-set-function");
+  const auto *Member = Result.Nodes.getNodeAs<FieldDecl>("member");
+
+  diag(Found->getBeginLoc(),
+       "function \'%0\' can be mistakenly used in order to change the "
path: clang-tools-extra/clang-tidy/bugprone/MisleadingSetterOfReferenceCheck.cpp
user: login: vbvictor
body: We should also specify how a user can make code correct and avoid this warning. 
e.g. "function `setX` can be mistakenly used in order to change the reference `MemX` instead of the value of it; consider changing type of parameter `NewValue` to `X`".
AFAIK this is desired behavior of `setX`.

created_at: 2025-04-01T07:56:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,58 @@
+//===--- MisleadingSetterOfReferenceCheck.cpp - clang-tidy-----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MisleadingSetterOfReferenceCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+void MisleadingSetterOfReferenceCheck::registerMatchers(MatchFinder *Finder) {
+  auto RefField =
+      fieldDecl(unless(isPublic()),
+                hasType(referenceType(pointee(equalsBoundNode("type")))))
+          .bind("member");
+  auto AssignLHS =
+      memberExpr(hasObjectExpression(cxxThisExpr()), member(RefField));
+  auto DerefOperand = expr(ignoringParenImpCasts(
+      declRefExpr(to(parmVarDecl(equalsBoundNode("parm"))))));
+  auto AssignRHS = expr(ignoringParenImpCasts(
+      unaryOperator(hasOperatorName("*"), hasUnaryOperand(DerefOperand))));
+
+  auto BinaryOpAssign = binaryOperator(hasOperatorName("="), hasLHS(AssignLHS),
+                                       hasRHS(AssignRHS));
+  auto CXXOperatorCallAssign = cxxOperatorCallExpr(
+      hasOverloadedOperatorName("="), hasLHS(AssignLHS), hasRHS(AssignRHS));
+
+  auto SetBody =
+      compoundStmt(statementCountIs(1),
+                   anyOf(has(BinaryOpAssign), has(CXXOperatorCallAssign)));
+  auto BadSetFunction =
+      cxxMethodDecl(parameterCountIs(1), isPublic(),
+                    hasAnyParameter(parmVarDecl(hasType(pointerType(pointee(
path: clang-tools-extra/clang-tidy/bugprone/MisleadingSetterOfReferenceCheck.cpp
user: login: vbvictor
body: ```suggestion
                    hasParameter(0, parmVarDecl(hasType(pointerType(pointee(
```
We know how many parameters there are
created_at: 2025-04-01T08:55:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,58 @@
+//===--- MisleadingSetterOfReferenceCheck.cpp - clang-tidy-----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MisleadingSetterOfReferenceCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+void MisleadingSetterOfReferenceCheck::registerMatchers(MatchFinder *Finder) {
+  auto RefField =
+      fieldDecl(unless(isPublic()),
+                hasType(referenceType(pointee(equalsBoundNode("type")))))
+          .bind("member");
+  auto AssignLHS =
+      memberExpr(hasObjectExpression(cxxThisExpr()), member(RefField));
+  auto DerefOperand = expr(ignoringParenImpCasts(
+      declRefExpr(to(parmVarDecl(equalsBoundNode("parm"))))));
+  auto AssignRHS = expr(ignoringParenImpCasts(
+      unaryOperator(hasOperatorName("*"), hasUnaryOperand(DerefOperand))));
+
+  auto BinaryOpAssign = binaryOperator(hasOperatorName("="), hasLHS(AssignLHS),
+                                       hasRHS(AssignRHS));
+  auto CXXOperatorCallAssign = cxxOperatorCallExpr(
+      hasOverloadedOperatorName("="), hasLHS(AssignLHS), hasRHS(AssignRHS));
+
+  auto SetBody =
+      compoundStmt(statementCountIs(1),
+                   anyOf(has(BinaryOpAssign), has(CXXOperatorCallAssign)));
+  auto BadSetFunction =
+      cxxMethodDecl(parameterCountIs(1), isPublic(),
path: clang-tools-extra/clang-tidy/bugprone/MisleadingSetterOfReferenceCheck.cpp
user: login: vbvictor
body: `isPublic` may not be necessary here imo
created_at: 2025-04-01T08:57:45Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,42 @@
+.. title:: clang-tidy - bugprone-misleading-setter-of-reference
+
+bugprone-misleading-setter-of-reference
+=======================================
+
+Finds setter-like functions that take a pointer parameter and set a (non-const)
path: clang-tools-extra/docs/clang-tidy/checks/bugprone/misleading-setter-of-reference.rst
user: login: vbvictor
body: Please place a newline after the first sentence. 
See `clang-tools-extra/docs/clang-tidy/checks/bugprone/capturing-this-in-member-variable.rst` for reference.
created_at: 2025-04-01T09:01:57Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,58 @@
+//===--- MisleadingSetterOfReferenceCheck.cpp - clang-tidy-----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MisleadingSetterOfReferenceCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+void MisleadingSetterOfReferenceCheck::registerMatchers(MatchFinder *Finder) {
+  auto RefField =
+      fieldDecl(unless(isPublic()),
path: clang-tools-extra/clang-tidy/bugprone/MisleadingSetterOfReferenceCheck.cpp
user: login: balazske
body: I think this condition makes sense because if the member is public it can be changed anyway and a warning for a set function does not cover other cases of change. (It can be another issue if there is a public member and a set function for it.)
created_at: 2025-04-01T10:18:04Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,58 @@
+//===--- MisleadingSetterOfReferenceCheck.cpp - clang-tidy-----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MisleadingSetterOfReferenceCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+void MisleadingSetterOfReferenceCheck::registerMatchers(MatchFinder *Finder) {
+  auto RefField =
+      fieldDecl(unless(isPublic()),
+                hasType(referenceType(pointee(equalsBoundNode("type")))))
+          .bind("member");
+  auto AssignLHS =
+      memberExpr(hasObjectExpression(cxxThisExpr()), member(RefField));
+  auto DerefOperand = expr(ignoringParenImpCasts(
+      declRefExpr(to(parmVarDecl(equalsBoundNode("parm"))))));
+  auto AssignRHS = expr(ignoringParenImpCasts(
+      unaryOperator(hasOperatorName("*"), hasUnaryOperand(DerefOperand))));
+
+  auto BinaryOpAssign = binaryOperator(hasOperatorName("="), hasLHS(AssignLHS),
+                                       hasRHS(AssignRHS));
+  auto CXXOperatorCallAssign = cxxOperatorCallExpr(
+      hasOverloadedOperatorName("="), hasLHS(AssignLHS), hasRHS(AssignRHS));
+
+  auto SetBody =
+      compoundStmt(statementCountIs(1),
+                   anyOf(has(BinaryOpAssign), has(CXXOperatorCallAssign)));
+  auto BadSetFunction =
+      cxxMethodDecl(parameterCountIs(1), isPublic(),
path: clang-tools-extra/clang-tidy/bugprone/MisleadingSetterOfReferenceCheck.cpp
user: login: balazske
body: The intent is to find cases when the reference is changed from outside the class with a public function.
created_at: 2025-04-01T10:20:04Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,58 @@
+//===--- MisleadingSetterOfReferenceCheck.cpp - clang-tidy-----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MisleadingSetterOfReferenceCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+void MisleadingSetterOfReferenceCheck::registerMatchers(MatchFinder *Finder) {
+  auto RefField =
+      fieldDecl(unless(isPublic()),
path: clang-tools-extra/clang-tidy/bugprone/MisleadingSetterOfReferenceCheck.cpp
user: login: NagyDonat
body: I agree that those cases (where a public reference member has a setter function with misleading type) are "twice buggy" (because public members should not have a setter function in any case), but this means that they are very unlikely to appear in the analyzed code, so we shouldn't complicate the behavior of this checker with extra code for it.

As we discussed on the meeting, please remove the `unless(isPublic())` condition.
created_at: 2025-04-24T14:42:03Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,50 @@
+// RUN: %check_clang_tidy %s bugprone-misleading-setter-of-reference %t
path: clang-tools-extra/test/clang-tidy/checkers/bugprone/misleading-setter-of-reference.cpp
user: login: balazske
body: Added tests for inheritance and templates.
created_at: 2025-05-06T10:28:59Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,58 @@
+//===--- MisleadingSetterOfReferenceCheck.cpp - clang-tidy-----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MisleadingSetterOfReferenceCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+void MisleadingSetterOfReferenceCheck::registerMatchers(MatchFinder *Finder) {
+  auto RefField =
+      fieldDecl(unless(isPublic()),
path: clang-tools-extra/clang-tidy/bugprone/MisleadingSetterOfReferenceCheck.cpp
user: login: balazske
body: I have removed it now.
created_at: 2025-05-06T10:55:55Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,58 @@
+//===--- MisleadingSetterOfReferenceCheck.cpp - clang-tidy-----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MisleadingSetterOfReferenceCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+void MisleadingSetterOfReferenceCheck::registerMatchers(MatchFinder *Finder) {
+  auto RefField =
+      fieldDecl(unless(isPublic()),
path: clang-tools-extra/clang-tidy/bugprone/MisleadingSetterOfReferenceCheck.cpp
user: login: NagyDonat
body: Thanks!
created_at: 2025-05-06T13:18:32Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,47 @@
+.. title:: clang-tidy - bugprone-misleading-setter-of-reference
+
+bugprone-misleading-setter-of-reference
+=======================================
+
+Finds setter-like member functions that take a pointer parameter and set a
+(non-const) reference member of the same class with the pointed value.
+
+The checker detects public member functions that have a single parameter (which
+is a pointer) and contain a single (maybe overloaded) assignment operator call.
+The assignment should set a member variable with the dereference of the
+parameter pointer. The member variable can have any visibility.
path: clang-tools-extra/docs/clang-tidy/checks/bugprone/misleading-setter-of-reference.rst
user: login: NagyDonat
body: ```suggestion
The checker detects public member functions that take a single pointer
parameter, and contain a single expression statement that dereferences the
parameter and assigns the result to a data member with a reference type.
```
This paragraph was a bit unclear, because it tries to describe a very complex situation. I tried to reword it (with some help from Endre) to make it easier to understand. 

In my suggestion I don't explicitly mention "(maybe overloaded)" and "any visibility" because those are the defaults from the point of view from the user: in theory overloaded assignment operators are equivalent to built-in ones and if we don't speak about constraints on the visibility of a data member, then we imply that it can have any visibility.
created_at: 2025-05-06T13:39:45Z
author_association: CONTRIBUTOR
diff_hunk: @@ -6,42 +6,42 @@ bugprone-misleading-setter-of-reference
 Finds setter-like member functions that take a pointer parameter and set a
 (non-const) reference member of the same class with the pointed value.
 
+The checker detects public member functions that have a single parameter (which
+is a pointer) and contain a single (maybe overloaded) assignment operator call.
+The assignment should set a member variable with the dereference of the
+parameter pointer. The member variable can have any visibility.
+
 The fact that a setter function takes a pointer might cause the belief that an
 internal reference (if it would be a pointer) is changed instead of the
 pointed-to (or referenced) value.
 
-Only member functions are detected which have a single parameter and contain a
-single (maybe overloaded) assignment operator call. The changed member variable
-must be private (or protected) for the checker to detect the fault (a public
-member can be changed anyway without a set function).
-
 Example:
 
 .. code-block:: c++
 
-  class Widget {
-    int& ref_;  // non-const reference member
+  class MyClass {
+    int &InternalRef;  // non-const reference member
   public:
-    Widget(int &value) : ref_(value) {}
+    MyClass(int &Value) : InternalRef(Value) {}
 
-    // Warning: Potentially dangerous setter that could lead to unintended behaviour
-    void setRef(int *ptr) {
-        ref_ = *ptr;  // This assigns to the referenced value, not changing what ref_ references
+    // Warning: This setter could lead to unintended behaviour.
+    void setRef(int *Value) {
+      InternalRef = *Value;  // This assigns to the referenced value, not changing what ref_ references.
path: clang-tools-extra/docs/clang-tidy/checks/bugprone/misleading-setter-of-reference.rst
user: login: NagyDonat
body: The comment still uses the old name `ref_`, please update it.
created_at: 2025-05-06T13:40:45Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,47 @@
+.. title:: clang-tidy - bugprone-misleading-setter-of-reference
+
+bugprone-misleading-setter-of-reference
+=======================================
+
+Finds setter-like member functions that take a pointer parameter and set a
+(non-const) reference member of the same class with the pointed value.
+
+The checker detects public member functions that have a single parameter (which
+is a pointer) and contain a single (maybe overloaded) assignment operator call.
+The assignment should set a member variable with the dereference of the
+parameter pointer. The member variable can have any visibility.
+
+The fact that a setter function takes a pointer might cause the belief that an
+internal reference (if it would be a pointer) is changed instead of the
+pointed-to (or referenced) value.
+
+Example:
+
+.. code-block:: c++
+
+  class MyClass {
+    int &InternalRef;  // non-const reference member
+  public:
+    MyClass(int &Value) : InternalRef(Value) {}
+
+    // Warning: This setter could lead to unintended behaviour.
+    void setRef(int *Value) {
+      InternalRef = *Value;  // This assigns to the referenced value, not changing what ref_ references.
+    }
+  };
+
+  int main() {
+    int Value1 = 42;
+    int Value2 = 100;
+    MyClass X(Value1);
+    
+    // This might look like it changes what InternalRef references to,
+    // but it actually modifies Value1 to be 100.
+    X.setRef(&Value2);
+  }
+
+Possible fixes:
+  - Change the parameter type of the "set" function to non-pointer or const reference
+    type.
path: clang-tools-extra/docs/clang-tidy/checks/bugprone/misleading-setter-of-reference.rst
user: login: NagyDonat
body: ```suggestion
  - Change the parameter type of the "set" function to a non-pointer type (for
    example, a const reference).
```
"non-pointer or const reference type" was strange, because const references are a subset of non-pointer types.
created_at: 2025-05-06T13:43:25Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,58 @@
+//===--- MisleadingSetterOfReferenceCheck.cpp - clang-tidy-----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MisleadingSetterOfReferenceCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+void MisleadingSetterOfReferenceCheck::registerMatchers(MatchFinder *Finder) {
+  auto RefField =
+      fieldDecl(unless(isPublic()),
+                hasType(referenceType(pointee(equalsBoundNode("type")))))
+          .bind("member");
+  auto AssignLHS =
+      memberExpr(hasObjectExpression(cxxThisExpr()), member(RefField));
+  auto DerefOperand = expr(ignoringParenImpCasts(
+      declRefExpr(to(parmVarDecl(equalsBoundNode("parm"))))));
+  auto AssignRHS = expr(ignoringParenImpCasts(
+      unaryOperator(hasOperatorName("*"), hasUnaryOperand(DerefOperand))));
+
+  auto BinaryOpAssign = binaryOperator(hasOperatorName("="), hasLHS(AssignLHS),
+                                       hasRHS(AssignRHS));
+  auto CXXOperatorCallAssign = cxxOperatorCallExpr(
+      hasOverloadedOperatorName("="), hasLHS(AssignLHS), hasRHS(AssignRHS));
+
+  auto SetBody =
+      compoundStmt(statementCountIs(1),
+                   anyOf(has(BinaryOpAssign), has(CXXOperatorCallAssign)));
+  auto BadSetFunction =
+      cxxMethodDecl(parameterCountIs(1), isPublic(),
path: clang-tools-extra/clang-tidy/bugprone/MisleadingSetterOfReferenceCheck.cpp
user: login: NagyDonat
body: I support keeping `isPublic` because this check essentially looks for _classes with a misleading API_, and these functions are bugprone because they can mislead somebody who only looks at the public signature without reviewing the actual implementation -- and these don't apply for private methods.
created_at: 2025-05-06T13:53:59Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,42 @@
+.. title:: clang-tidy - bugprone-misleading-setter-of-reference
+
+bugprone-misleading-setter-of-reference
+=======================================
+
+Finds setter-like functions that take a pointer parameter and set a (non-const)
path: clang-tools-extra/docs/clang-tidy/checks/bugprone/misleading-setter-of-reference.rst
user: login: NagyDonat
body: (I think this can be marked as resolved -- am I right?)
created_at: 2025-05-06T13:55:54Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,58 @@
+//===--- MisleadingSetterOfReferenceCheck.cpp - clang-tidy-----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MisleadingSetterOfReferenceCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+void MisleadingSetterOfReferenceCheck::registerMatchers(MatchFinder *Finder) {
+  auto RefField =
+      fieldDecl(unless(isPublic()),
+                hasType(referenceType(pointee(equalsBoundNode("type")))))
+          .bind("member");
+  auto AssignLHS =
+      memberExpr(hasObjectExpression(cxxThisExpr()), member(RefField));
+  auto DerefOperand = expr(ignoringParenImpCasts(
+      declRefExpr(to(parmVarDecl(equalsBoundNode("parm"))))));
+  auto AssignRHS = expr(ignoringParenImpCasts(
+      unaryOperator(hasOperatorName("*"), hasUnaryOperand(DerefOperand))));
+
+  auto BinaryOpAssign = binaryOperator(hasOperatorName("="), hasLHS(AssignLHS),
+                                       hasRHS(AssignRHS));
+  auto CXXOperatorCallAssign = cxxOperatorCallExpr(
+      hasOverloadedOperatorName("="), hasLHS(AssignLHS), hasRHS(AssignRHS));
+
+  auto SetBody =
+      compoundStmt(statementCountIs(1),
+                   anyOf(has(BinaryOpAssign), has(CXXOperatorCallAssign)));
+  auto BadSetFunction =
+      cxxMethodDecl(parameterCountIs(1), isPublic(),
path: clang-tools-extra/clang-tidy/bugprone/MisleadingSetterOfReferenceCheck.cpp
user: login: vbvictor
body: Consider this example https://godbolt.org/z/9xd4j9ef5, I can call a protected method as a public, maybe add `isProteced()` as well?

created_at: 2025-05-06T14:04:04Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,58 @@
+//===--- MisleadingSetterOfReferenceCheck.cpp - clang-tidy-----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MisleadingSetterOfReferenceCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+void MisleadingSetterOfReferenceCheck::registerMatchers(MatchFinder *Finder) {
+  auto RefField =
+      fieldDecl(unless(isPublic()),
+                hasType(referenceType(pointee(equalsBoundNode("type")))))
+          .bind("member");
+  auto AssignLHS =
+      memberExpr(hasObjectExpression(cxxThisExpr()), member(RefField));
+  auto DerefOperand = expr(ignoringParenImpCasts(
+      declRefExpr(to(parmVarDecl(equalsBoundNode("parm"))))));
+  auto AssignRHS = expr(ignoringParenImpCasts(
+      unaryOperator(hasOperatorName("*"), hasUnaryOperand(DerefOperand))));
+
+  auto BinaryOpAssign = binaryOperator(hasOperatorName("="), hasLHS(AssignLHS),
+                                       hasRHS(AssignRHS));
+  auto CXXOperatorCallAssign = cxxOperatorCallExpr(
+      hasOverloadedOperatorName("="), hasLHS(AssignLHS), hasRHS(AssignRHS));
+
+  auto SetBody =
+      compoundStmt(statementCountIs(1),
+                   anyOf(has(BinaryOpAssign), has(CXXOperatorCallAssign)));
+  auto BadSetFunction =
+      cxxMethodDecl(parameterCountIs(1), isPublic(),
path: clang-tools-extra/clang-tidy/bugprone/MisleadingSetterOfReferenceCheck.cpp
user: login: NagyDonat
body: I think exposing a protected setter method (which has this particular bug) as a public method of a derived class would be very rare, but I'm not opposed to adding `isProtected()` if you think that it would be more elegant.

I can also accept removing visibility constraints on the method -- private setter methods should be vanishingly rare in real code, so although excluding them makes sense from a theoretical/design POV, it doesn't provide practical benefits...
created_at: 2025-05-06T14:15:11Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,47 @@
+.. title:: clang-tidy - bugprone-misleading-setter-of-reference
+
+bugprone-misleading-setter-of-reference
+=======================================
+
+Finds setter-like member functions that take a pointer parameter and set a
+(non-const) reference member of the same class with the pointed value.
+
+The checker detects public member functions that have a single parameter (which
path: clang-tools-extra/docs/clang-tidy/checks/bugprone/misleading-setter-of-reference.rst
user: login: EugeneZelenko
body: ```suggestion
The check detects public member functions that have a single parameter (which
```

Clang-Tidy uses `check`.
created_at: 2025-05-06T14:24:00Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,58 @@
+//===--- MisleadingSetterOfReferenceCheck.cpp - clang-tidy-----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MisleadingSetterOfReferenceCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+void MisleadingSetterOfReferenceCheck::registerMatchers(MatchFinder *Finder) {
+  auto RefField =
+      fieldDecl(unless(isPublic()),
+                hasType(referenceType(pointee(equalsBoundNode("type")))))
+          .bind("member");
+  auto AssignLHS =
+      memberExpr(hasObjectExpression(cxxThisExpr()), member(RefField));
+  auto DerefOperand = expr(ignoringParenImpCasts(
+      declRefExpr(to(parmVarDecl(equalsBoundNode("parm"))))));
+  auto AssignRHS = expr(ignoringParenImpCasts(
+      unaryOperator(hasOperatorName("*"), hasUnaryOperand(DerefOperand))));
+
+  auto BinaryOpAssign = binaryOperator(hasOperatorName("="), hasLHS(AssignLHS),
+                                       hasRHS(AssignRHS));
+  auto CXXOperatorCallAssign = cxxOperatorCallExpr(
+      hasOverloadedOperatorName("="), hasLHS(AssignLHS), hasRHS(AssignRHS));
+
+  auto SetBody =
+      compoundStmt(statementCountIs(1),
+                   anyOf(has(BinaryOpAssign), has(CXXOperatorCallAssign)));
+  auto BadSetFunction =
+      cxxMethodDecl(parameterCountIs(1), isPublic(),
path: clang-tools-extra/clang-tidy/bugprone/MisleadingSetterOfReferenceCheck.cpp
user: login: vbvictor
body: I'm more in favor of removing any visibility.
Even if public API is OK, maybe the class author made a mistake in internal realization and now there is actually a bug. I think we should check for that as well.
created_at: 2025-05-07T09:03:26Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,58 @@
+//===--- MisleadingSetterOfReferenceCheck.cpp - clang-tidy-----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MisleadingSetterOfReferenceCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+void MisleadingSetterOfReferenceCheck::registerMatchers(MatchFinder *Finder) {
+  auto RefField =
+      fieldDecl(unless(isPublic()),
+                hasType(referenceType(pointee(equalsBoundNode("type")))))
+          .bind("member");
+  auto AssignLHS =
+      memberExpr(hasObjectExpression(cxxThisExpr()), member(RefField));
+  auto DerefOperand = expr(ignoringParenImpCasts(
+      declRefExpr(to(parmVarDecl(equalsBoundNode("parm"))))));
+  auto AssignRHS = expr(ignoringParenImpCasts(
+      unaryOperator(hasOperatorName("*"), hasUnaryOperand(DerefOperand))));
+
+  auto BinaryOpAssign = binaryOperator(hasOperatorName("="), hasLHS(AssignLHS),
+                                       hasRHS(AssignRHS));
+  auto CXXOperatorCallAssign = cxxOperatorCallExpr(
+      hasOverloadedOperatorName("="), hasLHS(AssignLHS), hasRHS(AssignRHS));
+
+  auto SetBody =
+      compoundStmt(statementCountIs(1),
+                   anyOf(has(BinaryOpAssign), has(CXXOperatorCallAssign)));
+  auto BadSetFunction =
+      cxxMethodDecl(parameterCountIs(1), isPublic(),
path: clang-tools-extra/clang-tidy/bugprone/MisleadingSetterOfReferenceCheck.cpp
user: login: balazske
body: I removed it now, this function design is "suspicious" even if protected or private because it is always better to pass the parameter by value or reference.
created_at: 2025-05-07T10:17:13Z
author_association: COLLABORATOR

PR comments:
user: login: llvmbot
created_at: 2025-03-20T15:50:39Z
body: 
<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang-tools-extra

Author: Balázs Kéri (balazske)

<details>
<summary>Changes</summary>

There can be concerns about the usefulness of this check but for some code it can be useful.

---
Full diff: https://github.com/llvm/llvm-project/pull/132242.diff


6 Files Affected:

- (modified) clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp (+3) 
- (modified) clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt (+1) 
- (added) clang-tools-extra/clang-tidy/bugprone/MisleadingSetterOfReferenceCheck.cpp (+58) 
- (added) clang-tools-extra/clang-tidy/bugprone/MisleadingSetterOfReferenceCheck.h (+37) 
- (added) clang-tools-extra/docs/clang-tidy/checks/bugprone/misleading-setter-of-reference.rst (+42) 
- (added) clang-tools-extra/test/clang-tidy/checkers/bugprone/misleading-setter-of-reference.cpp (+50) 


``````````diff
diff --git a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
index b780a85bdf3fe..64f4a524daf0d 100644
--- a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
@@ -41,6 +41,7 @@
 #include "LambdaFunctionNameCheck.h"
 #include "MacroParenthesesCheck.h"
 #include "MacroRepeatedSideEffectsCheck.h"
+#include "MisleadingSetterOfReferenceCheck.h"
 #include "MisplacedOperatorInStrlenInAllocCheck.h"
 #include "MisplacedPointerArithmeticInAllocCheck.h"
 #include "MisplacedWideningCastCheck.h"
@@ -170,6 +171,8 @@ class BugproneModule : public ClangTidyModule {
         "bugprone-macro-parentheses");
     CheckFactories.registerCheck<MacroRepeatedSideEffectsCheck>(
         "bugprone-macro-repeated-side-effects");
+    CheckFactories.registerCheck<MisleadingSetterOfReferenceCheck>(
+        "bugprone-misleading-setter-of-reference");
     CheckFactories.registerCheck<MisplacedOperatorInStrlenInAllocCheck>(
         "bugprone-misplaced-operator-in-strlen-in-alloc");
     CheckFactories.registerCheck<MisplacedPointerArithmeticInAllocCheck>(
diff --git a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
index e310ea9c94543..d862794cde323 100644
--- a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
@@ -42,6 +42,7 @@ add_clang_library(clangTidyBugproneModule STATIC
   LambdaFunctionNameCheck.cpp
   MacroParenthesesCheck.cpp
   MacroRepeatedSideEffectsCheck.cpp
+  MisleadingSetterOfReferenceCheck.cpp
   MisplacedOperatorInStrlenInAllocCheck.cpp
   MisplacedPointerArithmeticInAllocCheck.cpp
   MisplacedWideningCastCheck.cpp
diff --git a/clang-tools-extra/clang-tidy/bugprone/MisleadingSetterOfReferenceCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/MisleadingSetterOfReferenceCheck.cpp
new file mode 100644
index 0000000000000..043d15e7fead2
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/MisleadingSetterOfReferenceCheck.cpp
@@ -0,0 +1,58 @@
+//===--- MisleadingSetterOfReferenceCheck.cpp - clang-tidy-----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MisleadingSetterOfReferenceCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+void MisleadingSetterOfReferenceCheck::registerMatchers(MatchFinder *Finder) {
+  auto RefField =
+      fieldDecl(unless(isPublic()),
+                hasType(referenceType(pointee(equalsBoundNode("type")))))
+          .bind("member");
+  auto AssignLHS =
+      memberExpr(hasObjectExpression(cxxThisExpr()), member(RefField));
+  auto DerefOperand = expr(ignoringParenImpCasts(
+      declRefExpr(to(parmVarDecl(equalsBoundNode("parm"))))));
+  auto AssignRHS = expr(ignoringParenImpCasts(
+      unaryOperator(hasOperatorName("*"), hasUnaryOperand(DerefOperand))));
+
+  auto BinaryOpAssign = binaryOperator(hasOperatorName("="), hasLHS(AssignLHS),
+                                       hasRHS(AssignRHS));
+  auto CXXOperatorCallAssign = cxxOperatorCallExpr(
+      hasOverloadedOperatorName("="), hasLHS(AssignLHS), hasRHS(AssignRHS));
+
+  auto SetBody =
+      compoundStmt(statementCountIs(1),
+                   anyOf(has(BinaryOpAssign), has(CXXOperatorCallAssign)));
+  auto BadSetFunction =
+      cxxMethodDecl(parameterCountIs(1), isPublic(),
+                    hasAnyParameter(parmVarDecl(hasType(pointerType(pointee(
+                                                    qualType().bind("type")))))
+                                        .bind("parm")),
+                    hasBody(SetBody))
+          .bind("bad-set-function");
+  Finder->addMatcher(BadSetFunction, this);
+}
+
+void MisleadingSetterOfReferenceCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  const auto *Found = Result.Nodes.getNodeAs<CXXMethodDecl>("bad-set-function");
+  const auto *Member = Result.Nodes.getNodeAs<FieldDecl>("member");
+
+  diag(Found->getBeginLoc(),
+       "function \'%0\' can be mistakenly used in order to change the "
+       "reference \'%1\' instead of the value of it")
+      << Found->getName() << Member->getName();
+}
+
+} // namespace clang::tidy::bugprone
diff --git a/clang-tools-extra/clang-tidy/bugprone/MisleadingSetterOfReferenceCheck.h b/clang-tools-extra/clang-tidy/bugprone/MisleadingSetterOfReferenceCheck.h
new file mode 100644
index 0000000000000..99e7a9435cfa9
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/MisleadingSetterOfReferenceCheck.h
@@ -0,0 +1,37 @@
+//===--- MisleadingSetterOfReferenceCheck.h - clang-tidy---------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_MISLEADINGSETTEROFREFERENCECHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_MISLEADINGSETTEROFREFERENCECHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::bugprone {
+
+/// Emits a warning when a setter-like function that has a pointer parameter
+/// is used to set value of a field with reference type.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone/throw-keyword-missing.html
+class MisleadingSetterOfReferenceCheck : public ClangTidyCheck {
+public:
+  MisleadingSetterOfReferenceCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+    return LangOpts.CPlusPlus;
+  }
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+  std::optional<TraversalKind> getCheckTraversalKind() const override {
+    return TK_IgnoreUnlessSpelledInSource;
+  }
+};
+
+} // namespace clang::tidy::bugprone
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_MISLEADINGSETTEROFREFERENCECHECK_H
diff --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone/misleading-setter-of-reference.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone/misleading-setter-of-reference.rst
new file mode 100644
index 0000000000000..69c0d5530fb61
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone/misleading-setter-of-reference.rst
@@ -0,0 +1,42 @@
+.. title:: clang-tidy - bugprone-misleading-setter-of-reference
+
+bugprone-misleading-setter-of-reference
+=======================================
+
+Finds setter-like functions that take a pointer parameter and set a (non-const)
+reference with the pointed value. The fact that a setter function takes a
+pointer might cause the belief that an internal reference (if it would be a
+pointer) is changed instead of the pointed-to (or referenced) value.
+
+Only member functions are detected which have a single parameter and contain a
+single (maybe overloaded) assignment operator call.
+
+Example:
+
+.. code-block:: c++
+
+  class Widget {
+    int& ref_;  // non-const reference member
+  public:
+    // non-copyable
+    Widget(const Widget&) = delete;
+    // non-movable
+    Widget(Widget&&) = delete;
+ 
+    Widget(int& value) : ref_(value) {}
+    
+    // Potentially dangerous setter that could lead to unintended behaviour
+    void setRef(int* ptr) {
+        ref_ = *ptr;  // This assigns to the referenced value, not changing what ref_ references
+    }
+  };
+
+  int main() {
+    int value1 = 42;
+    int value2 = 100;
+    Widget w(value1);
+    
+    // This might look like it changes what ref_ references to,
+    // but it actually modifies value1 to be 100
+    w.setRef(&value2);  // value1 is now 100, ref_ still references value1
+  }
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/misleading-setter-of-reference.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/misleading-setter-of-reference.cpp
new file mode 100644
index 0000000000000..9f9a616824469
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/misleading-setter-of-reference.cpp
@@ -0,0 +1,50 @@
+// RUN: %check_clang_tidy %s bugprone-misleading-setter-of-reference %t
+
+struct X {
+  X &operator=(const X &) { return *this; }
+  int &Mem;
+};
+
+class Test1 {
+  X &MemX;
+  int &MemI;
+protected:
+  long &MemL;
+public:
+  long &MemLPub;
+
+  Test1(X &MemX, int &MemI, long &MemL) : MemX(MemX), MemI(MemI), MemL(MemL), MemLPub(MemL) {}
+  void setI(int *NewValue) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: function 'setI' can be mistakenly used in order to change the reference 'MemI' instead of the value of it [bugprone-misleading-setter-of-reference]
+    MemI = *NewValue;
+  }
+  void setL(long *NewValue) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: function 'setL' can be mistakenly used in order to change the reference 'MemL' instead of the value of it [bugprone-misleading-setter-of-reference]
+    MemL = *NewValue;
+  }
+  void setX(X *NewValue) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: function 'setX' can be mistakenly used in order to change the reference 'MemX' instead of the value of it [bugprone-misleading-setter-of-reference]
+    MemX = *NewValue;
+  }
+  void set1(int *NewValue) {
+    MemX.Mem = *NewValue;
+  }
+  void set2(int *NewValue) {
+    MemL = static_cast<long>(*NewValue);
+  }
+  void set3(int *NewValue) {
+    MemI = *NewValue;
+    MemL = static_cast<long>(*NewValue);
+  }
+  void set4(long *NewValue, int) {
+    MemL = *NewValue;
+  }
+  void setLPub(long *NewValue) {
+    MemLPub = *NewValue;
+  }
+
+protected:
+  void set5(long *NewValue) {
+    MemL = *NewValue;
+  }
+};

``````````

</details>

author_association: MEMBER
user: login: llvmbot
created_at: 2025-03-20T15:50:40Z
body: 
<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang-tidy

Author: Balázs Kéri (balazske)

<details>
<summary>Changes</summary>

There can be concerns about the usefulness of this check but for some code it can be useful.

---
Full diff: https://github.com/llvm/llvm-project/pull/132242.diff


6 Files Affected:

- (modified) clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp (+3) 
- (modified) clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt (+1) 
- (added) clang-tools-extra/clang-tidy/bugprone/MisleadingSetterOfReferenceCheck.cpp (+58) 
- (added) clang-tools-extra/clang-tidy/bugprone/MisleadingSetterOfReferenceCheck.h (+37) 
- (added) clang-tools-extra/docs/clang-tidy/checks/bugprone/misleading-setter-of-reference.rst (+42) 
- (added) clang-tools-extra/test/clang-tidy/checkers/bugprone/misleading-setter-of-reference.cpp (+50) 


``````````diff
diff --git a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
index b780a85bdf3fe..64f4a524daf0d 100644
--- a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
@@ -41,6 +41,7 @@
 #include "LambdaFunctionNameCheck.h"
 #include "MacroParenthesesCheck.h"
 #include "MacroRepeatedSideEffectsCheck.h"
+#include "MisleadingSetterOfReferenceCheck.h"
 #include "MisplacedOperatorInStrlenInAllocCheck.h"
 #include "MisplacedPointerArithmeticInAllocCheck.h"
 #include "MisplacedWideningCastCheck.h"
@@ -170,6 +171,8 @@ class BugproneModule : public ClangTidyModule {
         "bugprone-macro-parentheses");
     CheckFactories.registerCheck<MacroRepeatedSideEffectsCheck>(
         "bugprone-macro-repeated-side-effects");
+    CheckFactories.registerCheck<MisleadingSetterOfReferenceCheck>(
+        "bugprone-misleading-setter-of-reference");
     CheckFactories.registerCheck<MisplacedOperatorInStrlenInAllocCheck>(
         "bugprone-misplaced-operator-in-strlen-in-alloc");
     CheckFactories.registerCheck<MisplacedPointerArithmeticInAllocCheck>(
diff --git a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
index e310ea9c94543..d862794cde323 100644
--- a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
@@ -42,6 +42,7 @@ add_clang_library(clangTidyBugproneModule STATIC
   LambdaFunctionNameCheck.cpp
   MacroParenthesesCheck.cpp
   MacroRepeatedSideEffectsCheck.cpp
+  MisleadingSetterOfReferenceCheck.cpp
   MisplacedOperatorInStrlenInAllocCheck.cpp
   MisplacedPointerArithmeticInAllocCheck.cpp
   MisplacedWideningCastCheck.cpp
diff --git a/clang-tools-extra/clang-tidy/bugprone/MisleadingSetterOfReferenceCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/MisleadingSetterOfReferenceCheck.cpp
new file mode 100644
index 0000000000000..043d15e7fead2
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/MisleadingSetterOfReferenceCheck.cpp
@@ -0,0 +1,58 @@
+//===--- MisleadingSetterOfReferenceCheck.cpp - clang-tidy-----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MisleadingSetterOfReferenceCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+void MisleadingSetterOfReferenceCheck::registerMatchers(MatchFinder *Finder) {
+  auto RefField =
+      fieldDecl(unless(isPublic()),
+                hasType(referenceType(pointee(equalsBoundNode("type")))))
+          .bind("member");
+  auto AssignLHS =
+      memberExpr(hasObjectExpression(cxxThisExpr()), member(RefField));
+  auto DerefOperand = expr(ignoringParenImpCasts(
+      declRefExpr(to(parmVarDecl(equalsBoundNode("parm"))))));
+  auto AssignRHS = expr(ignoringParenImpCasts(
+      unaryOperator(hasOperatorName("*"), hasUnaryOperand(DerefOperand))));
+
+  auto BinaryOpAssign = binaryOperator(hasOperatorName("="), hasLHS(AssignLHS),
+                                       hasRHS(AssignRHS));
+  auto CXXOperatorCallAssign = cxxOperatorCallExpr(
+      hasOverloadedOperatorName("="), hasLHS(AssignLHS), hasRHS(AssignRHS));
+
+  auto SetBody =
+      compoundStmt(statementCountIs(1),
+                   anyOf(has(BinaryOpAssign), has(CXXOperatorCallAssign)));
+  auto BadSetFunction =
+      cxxMethodDecl(parameterCountIs(1), isPublic(),
+                    hasAnyParameter(parmVarDecl(hasType(pointerType(pointee(
+                                                    qualType().bind("type")))))
+                                        .bind("parm")),
+                    hasBody(SetBody))
+          .bind("bad-set-function");
+  Finder->addMatcher(BadSetFunction, this);
+}
+
+void MisleadingSetterOfReferenceCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  const auto *Found = Result.Nodes.getNodeAs<CXXMethodDecl>("bad-set-function");
+  const auto *Member = Result.Nodes.getNodeAs<FieldDecl>("member");
+
+  diag(Found->getBeginLoc(),
+       "function \'%0\' can be mistakenly used in order to change the "
+       "reference \'%1\' instead of the value of it")
+      << Found->getName() << Member->getName();
+}
+
+} // namespace clang::tidy::bugprone
diff --git a/clang-tools-extra/clang-tidy/bugprone/MisleadingSetterOfReferenceCheck.h b/clang-tools-extra/clang-tidy/bugprone/MisleadingSetterOfReferenceCheck.h
new file mode 100644
index 0000000000000..99e7a9435cfa9
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/MisleadingSetterOfReferenceCheck.h
@@ -0,0 +1,37 @@
+//===--- MisleadingSetterOfReferenceCheck.h - clang-tidy---------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_MISLEADINGSETTEROFREFERENCECHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_MISLEADINGSETTEROFREFERENCECHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::bugprone {
+
+/// Emits a warning when a setter-like function that has a pointer parameter
+/// is used to set value of a field with reference type.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone/throw-keyword-missing.html
+class MisleadingSetterOfReferenceCheck : public ClangTidyCheck {
+public:
+  MisleadingSetterOfReferenceCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+    return LangOpts.CPlusPlus;
+  }
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+  std::optional<TraversalKind> getCheckTraversalKind() const override {
+    return TK_IgnoreUnlessSpelledInSource;
+  }
+};
+
+} // namespace clang::tidy::bugprone
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_MISLEADINGSETTEROFREFERENCECHECK_H
diff --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone/misleading-setter-of-reference.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone/misleading-setter-of-reference.rst
new file mode 100644
index 0000000000000..69c0d5530fb61
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone/misleading-setter-of-reference.rst
@@ -0,0 +1,42 @@
+.. title:: clang-tidy - bugprone-misleading-setter-of-reference
+
+bugprone-misleading-setter-of-reference
+=======================================
+
+Finds setter-like functions that take a pointer parameter and set a (non-const)
+reference with the pointed value. The fact that a setter function takes a
+pointer might cause the belief that an internal reference (if it would be a
+pointer) is changed instead of the pointed-to (or referenced) value.
+
+Only member functions are detected which have a single parameter and contain a
+single (maybe overloaded) assignment operator call.
+
+Example:
+
+.. code-block:: c++
+
+  class Widget {
+    int& ref_;  // non-const reference member
+  public:
+    // non-copyable
+    Widget(const Widget&) = delete;
+    // non-movable
+    Widget(Widget&&) = delete;
+ 
+    Widget(int& value) : ref_(value) {}
+    
+    // Potentially dangerous setter that could lead to unintended behaviour
+    void setRef(int* ptr) {
+        ref_ = *ptr;  // This assigns to the referenced value, not changing what ref_ references
+    }
+  };
+
+  int main() {
+    int value1 = 42;
+    int value2 = 100;
+    Widget w(value1);
+    
+    // This might look like it changes what ref_ references to,
+    // but it actually modifies value1 to be 100
+    w.setRef(&value2);  // value1 is now 100, ref_ still references value1
+  }
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/misleading-setter-of-reference.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/misleading-setter-of-reference.cpp
new file mode 100644
index 0000000000000..9f9a616824469
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/misleading-setter-of-reference.cpp
@@ -0,0 +1,50 @@
+// RUN: %check_clang_tidy %s bugprone-misleading-setter-of-reference %t
+
+struct X {
+  X &operator=(const X &) { return *this; }
+  int &Mem;
+};
+
+class Test1 {
+  X &MemX;
+  int &MemI;
+protected:
+  long &MemL;
+public:
+  long &MemLPub;
+
+  Test1(X &MemX, int &MemI, long &MemL) : MemX(MemX), MemI(MemI), MemL(MemL), MemLPub(MemL) {}
+  void setI(int *NewValue) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: function 'setI' can be mistakenly used in order to change the reference 'MemI' instead of the value of it [bugprone-misleading-setter-of-reference]
+    MemI = *NewValue;
+  }
+  void setL(long *NewValue) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: function 'setL' can be mistakenly used in order to change the reference 'MemL' instead of the value of it [bugprone-misleading-setter-of-reference]
+    MemL = *NewValue;
+  }
+  void setX(X *NewValue) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: function 'setX' can be mistakenly used in order to change the reference 'MemX' instead of the value of it [bugprone-misleading-setter-of-reference]
+    MemX = *NewValue;
+  }
+  void set1(int *NewValue) {
+    MemX.Mem = *NewValue;
+  }
+  void set2(int *NewValue) {
+    MemL = static_cast<long>(*NewValue);
+  }
+  void set3(int *NewValue) {
+    MemI = *NewValue;
+    MemL = static_cast<long>(*NewValue);
+  }
+  void set4(long *NewValue, int) {
+    MemL = *NewValue;
+  }
+  void setLPub(long *NewValue) {
+    MemLPub = *NewValue;
+  }
+
+protected:
+  void set5(long *NewValue) {
+    MemL = *NewValue;
+  }
+};

``````````

</details>

author_association: MEMBER
user: login: EugeneZelenko
created_at: 2025-03-21T15:00:09Z
body: Please add entry in Release Notes.
author_association: CONTRIBUTOR
user: login: NagyDonat
created_at: 2025-05-06T13:48:20Z
body: The commit looks promising, my only remaining remarks are about phrasing in the documentation and comments.
author_association: CONTRIBUTOR
user: login: NagyDonat
created_at: 2025-05-06T14:08:48Z
body: Also @vbvictor thanks for your helpful remarks. As far as I see they were all addressed by @balazske -- do you have anything to add?

(Oops, I see that you added some remarks while I was writing this...)
author_association: CONTRIBUTOR
user: login: vbvictor
created_at: 2025-05-06T14:08:53Z
body: Please add entry to `clang-tools-extra/docs/ReleaseNotes.rst`. It should be the same as first sentence in checks docs.
author_association: CONTRIBUTOR
user: login: llvm-ci
created_at: 2025-05-17T08:39:19Z
body: LLVM Buildbot has detected a new failure on builder `clang-aarch64-quick` running on `linaro-clang-aarch64-quick` while building `clang-tools-extra` at step 5 "ninja check 1".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/65/builds/16760

<details>
<summary>Here is the relevant piece of the build log for the reference</summary>

```
Step 5 (ninja check 1) failure: stage 1 checked (failure)
******************** TEST 'lit :: timeout-hang.py' FAILED ********************
Exit Code: 1

Command Output (stdout):
--
# RUN: at line 13
not env -u FILECHECK_OPTS "/usr/bin/python3.10" /home/tcwg-buildbot/worker/clang-aarch64-quick/llvm/llvm/utils/lit/lit.py -j1 --order=lexical Inputs/timeout-hang/run-nonexistent.txt  --timeout=1 --param external=0 | "/usr/bin/python3.10" /home/tcwg-buildbot/worker/clang-aarch64-quick/stage1/utils/lit/tests/timeout-hang.py 1
# executed command: not env -u FILECHECK_OPTS /usr/bin/python3.10 /home/tcwg-buildbot/worker/clang-aarch64-quick/llvm/llvm/utils/lit/lit.py -j1 --order=lexical Inputs/timeout-hang/run-nonexistent.txt --timeout=1 --param external=0
# .---command stderr------------
# | lit.py: /home/tcwg-buildbot/worker/clang-aarch64-quick/llvm/llvm/utils/lit/lit/main.py:72: note: The test suite configuration requested an individual test timeout of 0 seconds but a timeout of 1 seconds was requested on the command line. Forcing timeout to be 1 seconds.
# `-----------------------------
# executed command: /usr/bin/python3.10 /home/tcwg-buildbot/worker/clang-aarch64-quick/stage1/utils/lit/tests/timeout-hang.py 1
# .---command stdout------------
# | Testing took as long or longer than timeout
# `-----------------------------
# error: command failed with exit status: 1

--

********************


```

</details>
author_association: NONE