Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

[clang-tidy] Add check bugprone-misleading-setter-of-reference #132242

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
May 17, 2025

Conversation

balazske
Copy link
Collaborator

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

@llvmbot
Copy link
Member

llvmbot commented Mar 20, 2025

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

Author: Balázs Kéri (balazske)

Changes

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 --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;
+  }
+};

@llvmbot
Copy link
Member

llvmbot commented Mar 20, 2025

@llvm/pr-subscribers-clang-tidy

Author: Balázs Kéri (balazske)

Changes

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 --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;
+  }
+};

@EugeneZelenko
Copy link
Contributor

Please add entry in Release Notes.

Copy link
Contributor

@NagyDonat NagyDonat left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall looks good to me; see inline comments for minor inline suggestions.

@@ -0,0 +1,50 @@
// RUN: %check_clang_tidy %s bugprone-misleading-setter-of-reference %t
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding tests for inheritance. E.g. Base class has method SetX and Derived calls it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, a test with templated class and templated method could be useful.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added tests for inheritance and templates.

const auto *Member = Result.Nodes.getNodeAs<FieldDecl>("member");

diag(Found->getBeginLoc(),
"function \'%0\' can be mistakenly used in order to change the "
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

compoundStmt(statementCountIs(1),
anyOf(has(BinaryOpAssign), has(CXXOperatorCallAssign)));
auto BadSetFunction =
cxxMethodDecl(parameterCountIs(1), isPublic(),
Copy link
Contributor

@vbvictor vbvictor Apr 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isPublic may not be necessary here imo

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The intent is to find cases when the reference is changed from outside the class with a public function.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider this example https://godbolt.org/z/9xd4j9ef5, I can call a protected method as a public, maybe add isProteced() as well?

Copy link
Contributor

@NagyDonat NagyDonat May 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@balazske balazske requested a review from Discookie April 23, 2025 08:52
@NagyDonat
Copy link
Contributor

The commit looks promising, my only remaining remarks are about phrasing in the documentation and comments.

compoundStmt(statementCountIs(1),
anyOf(has(BinaryOpAssign), has(CXXOperatorCallAssign)));
auto BadSetFunction =
cxxMethodDecl(parameterCountIs(1), isPublic(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@NagyDonat
Copy link
Contributor

NagyDonat commented May 6, 2025

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...)

@vbvictor
Copy link
Contributor

vbvictor commented May 6, 2025

Please add entry to clang-tools-extra/docs/ReleaseNotes.rst. It should be the same as first sentence in checks docs.

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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The checker detects public member functions that have a single parameter (which
The check detects public member functions that have a single parameter (which

Clang-Tidy uses check.

Copy link
Contributor

@NagyDonat NagyDonat left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks for the updates!

@balazske balazske merged commit d84b97e into llvm:main May 17, 2025
12 checks passed
@llvm-ci
Copy link
Collaborator

llvm-ci commented May 17, 2025

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

Here is the relevant piece of the build log for the reference
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

--

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


Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants
Morty Proxy This is a proxified and sanitized view of the page, visit original site.