-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[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
balazske
merged 7 commits into
llvm:main
from
balazske:suspicious-setter-of-reference-checker
May 17, 2025
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
e3064b6
[clang-tidy] Add check bugprone-misleading-setter-of-reference
balazske 2a7b326
updates from review and improvements
balazske 8f2c2d0
small documentation fix
balazske 9babb88
try to fix buildbot failure
balazske 9c2306e
removed 'isPublic' condition, improved documentation
balazske ce9dfb1
more review suggestion fixes
balazske d0b0776
removed isPublic function condition
balazske File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
61 changes: 61 additions & 0 deletions
61
clang-tools-extra/clang-tidy/bugprone/MisleadingSetterOfReferenceCheck.cpp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
37 changes: 37 additions & 0 deletions
37
clang-tools-extra/clang-tidy/bugprone/MisleadingSetterOfReferenceCheck.h
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
46 changes: 46 additions & 0 deletions
46
...-tools-extra/docs/clang-tidy/checks/bugprone/misleading-setter-of-reference.rst
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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). |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
121 changes: 121 additions & 0 deletions
121
clang-tools-extra/test/clang-tidy/checkers/bugprone/misleading-setter-of-reference.cpp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} | ||
}; |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
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 methodSetX
andDerived
calls it.There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.