Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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>(
Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ add_clang_library(clangTidyBugproneModule STATIC
LambdaFunctionNameCheck.cpp
MacroParenthesesCheck.cpp
MacroRepeatedSideEffectsCheck.cpp
MisleadingSetterOfReferenceCheck.cpp
MisplacedOperatorInStrlenInAllocCheck.cpp
MisplacedPointerArithmeticInAllocCheck.cpp
MisplacedWideningCastCheck.cpp
Expand Down
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), isPublic(),
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
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
Original file line number Diff line number Diff line change
@@ -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
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.

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.
- Change the type of the member variable to a pointer and in the "set"
function assign a value to the pointer (without dereference).
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
// 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.


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

protected:
void set5(long *NewValue) {
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;
}
};

char CharValue;
TemplateTest<char> TTChar{CharValue};

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