Skip to content

Commit e3064b6

Browse files
committed
[clang-tidy] Add check bugprone-misleading-setter-of-reference
1 parent 0753244 commit e3064b6

File tree

6 files changed

+191
-0
lines changed

6 files changed

+191
-0
lines changed

clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
#include "LambdaFunctionNameCheck.h"
4242
#include "MacroParenthesesCheck.h"
4343
#include "MacroRepeatedSideEffectsCheck.h"
44+
#include "MisleadingSetterOfReferenceCheck.h"
4445
#include "MisplacedOperatorInStrlenInAllocCheck.h"
4546
#include "MisplacedPointerArithmeticInAllocCheck.h"
4647
#include "MisplacedWideningCastCheck.h"
@@ -170,6 +171,8 @@ class BugproneModule : public ClangTidyModule {
170171
"bugprone-macro-parentheses");
171172
CheckFactories.registerCheck<MacroRepeatedSideEffectsCheck>(
172173
"bugprone-macro-repeated-side-effects");
174+
CheckFactories.registerCheck<MisleadingSetterOfReferenceCheck>(
175+
"bugprone-misleading-setter-of-reference");
173176
CheckFactories.registerCheck<MisplacedOperatorInStrlenInAllocCheck>(
174177
"bugprone-misplaced-operator-in-strlen-in-alloc");
175178
CheckFactories.registerCheck<MisplacedPointerArithmeticInAllocCheck>(

clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ add_clang_library(clangTidyBugproneModule STATIC
4242
LambdaFunctionNameCheck.cpp
4343
MacroParenthesesCheck.cpp
4444
MacroRepeatedSideEffectsCheck.cpp
45+
MisleadingSetterOfReferenceCheck.cpp
4546
MisplacedOperatorInStrlenInAllocCheck.cpp
4647
MisplacedPointerArithmeticInAllocCheck.cpp
4748
MisplacedWideningCastCheck.cpp
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
//===--- MisleadingSetterOfReferenceCheck.cpp - clang-tidy-----------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "MisleadingSetterOfReferenceCheck.h"
10+
#include "clang/AST/ASTContext.h"
11+
#include "clang/ASTMatchers/ASTMatchFinder.h"
12+
13+
using namespace clang::ast_matchers;
14+
15+
namespace clang::tidy::bugprone {
16+
17+
void MisleadingSetterOfReferenceCheck::registerMatchers(MatchFinder *Finder) {
18+
auto RefField =
19+
fieldDecl(unless(isPublic()),
20+
hasType(referenceType(pointee(equalsBoundNode("type")))))
21+
.bind("member");
22+
auto AssignLHS =
23+
memberExpr(hasObjectExpression(cxxThisExpr()), member(RefField));
24+
auto DerefOperand = expr(ignoringParenImpCasts(
25+
declRefExpr(to(parmVarDecl(equalsBoundNode("parm"))))));
26+
auto AssignRHS = expr(ignoringParenImpCasts(
27+
unaryOperator(hasOperatorName("*"), hasUnaryOperand(DerefOperand))));
28+
29+
auto BinaryOpAssign = binaryOperator(hasOperatorName("="), hasLHS(AssignLHS),
30+
hasRHS(AssignRHS));
31+
auto CXXOperatorCallAssign = cxxOperatorCallExpr(
32+
hasOverloadedOperatorName("="), hasLHS(AssignLHS), hasRHS(AssignRHS));
33+
34+
auto SetBody =
35+
compoundStmt(statementCountIs(1),
36+
anyOf(has(BinaryOpAssign), has(CXXOperatorCallAssign)));
37+
auto BadSetFunction =
38+
cxxMethodDecl(parameterCountIs(1), isPublic(),
39+
hasAnyParameter(parmVarDecl(hasType(pointerType(pointee(
40+
qualType().bind("type")))))
41+
.bind("parm")),
42+
hasBody(SetBody))
43+
.bind("bad-set-function");
44+
Finder->addMatcher(BadSetFunction, this);
45+
}
46+
47+
void MisleadingSetterOfReferenceCheck::check(
48+
const MatchFinder::MatchResult &Result) {
49+
const auto *Found = Result.Nodes.getNodeAs<CXXMethodDecl>("bad-set-function");
50+
const auto *Member = Result.Nodes.getNodeAs<FieldDecl>("member");
51+
52+
diag(Found->getBeginLoc(),
53+
"function \'%0\' can be mistakenly used in order to change the "
54+
"reference \'%1\' instead of the value of it")
55+
<< Found->getName() << Member->getName();
56+
}
57+
58+
} // namespace clang::tidy::bugprone
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
//===--- MisleadingSetterOfReferenceCheck.h - clang-tidy---------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_MISLEADINGSETTEROFREFERENCECHECK_H
10+
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_MISLEADINGSETTEROFREFERENCECHECK_H
11+
12+
#include "../ClangTidyCheck.h"
13+
14+
namespace clang::tidy::bugprone {
15+
16+
/// Emits a warning when a setter-like function that has a pointer parameter
17+
/// is used to set value of a field with reference type.
18+
///
19+
/// For the user-facing documentation see:
20+
/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone/throw-keyword-missing.html
21+
class MisleadingSetterOfReferenceCheck : public ClangTidyCheck {
22+
public:
23+
MisleadingSetterOfReferenceCheck(StringRef Name, ClangTidyContext *Context)
24+
: ClangTidyCheck(Name, Context) {}
25+
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
26+
return LangOpts.CPlusPlus;
27+
}
28+
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
29+
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
30+
std::optional<TraversalKind> getCheckTraversalKind() const override {
31+
return TK_IgnoreUnlessSpelledInSource;
32+
}
33+
};
34+
35+
} // namespace clang::tidy::bugprone
36+
37+
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_MISLEADINGSETTEROFREFERENCECHECK_H
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
.. title:: clang-tidy - bugprone-misleading-setter-of-reference
2+
3+
bugprone-misleading-setter-of-reference
4+
=======================================
5+
6+
Finds setter-like functions that take a pointer parameter and set a (non-const)
7+
reference with the pointed value. The fact that a setter function takes a
8+
pointer might cause the belief that an internal reference (if it would be a
9+
pointer) is changed instead of the pointed-to (or referenced) value.
10+
11+
Only member functions are detected which have a single parameter and contain a
12+
single (maybe overloaded) assignment operator call.
13+
14+
Example:
15+
16+
.. code-block:: c++
17+
18+
class Widget {
19+
int& ref_; // non-const reference member
20+
public:
21+
// non-copyable
22+
Widget(const Widget&) = delete;
23+
// non-movable
24+
Widget(Widget&&) = delete;
25+
26+
Widget(int& value) : ref_(value) {}
27+
28+
// Potentially dangerous setter that could lead to unintended behaviour
29+
void setRef(int* ptr) {
30+
ref_ = *ptr; // This assigns to the referenced value, not changing what ref_ references
31+
}
32+
};
33+
34+
int main() {
35+
int value1 = 42;
36+
int value2 = 100;
37+
Widget w(value1);
38+
39+
// This might look like it changes what ref_ references to,
40+
// but it actually modifies value1 to be 100
41+
w.setRef(&value2); // value1 is now 100, ref_ still references value1
42+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// RUN: %check_clang_tidy %s bugprone-misleading-setter-of-reference %t
2+
3+
struct X {
4+
X &operator=(const X &) { return *this; }
5+
int &Mem;
6+
};
7+
8+
class Test1 {
9+
X &MemX;
10+
int &MemI;
11+
protected:
12+
long &MemL;
13+
public:
14+
long &MemLPub;
15+
16+
Test1(X &MemX, int &MemI, long &MemL) : MemX(MemX), MemI(MemI), MemL(MemL), MemLPub(MemL) {}
17+
void setI(int *NewValue) {
18+
// 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]
19+
MemI = *NewValue;
20+
}
21+
void setL(long *NewValue) {
22+
// 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]
23+
MemL = *NewValue;
24+
}
25+
void setX(X *NewValue) {
26+
// 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]
27+
MemX = *NewValue;
28+
}
29+
void set1(int *NewValue) {
30+
MemX.Mem = *NewValue;
31+
}
32+
void set2(int *NewValue) {
33+
MemL = static_cast<long>(*NewValue);
34+
}
35+
void set3(int *NewValue) {
36+
MemI = *NewValue;
37+
MemL = static_cast<long>(*NewValue);
38+
}
39+
void set4(long *NewValue, int) {
40+
MemL = *NewValue;
41+
}
42+
void setLPub(long *NewValue) {
43+
MemLPub = *NewValue;
44+
}
45+
46+
protected:
47+
void set5(long *NewValue) {
48+
MemL = *NewValue;
49+
}
50+
};

0 commit comments

Comments
 (0)