Skip to content

[clang-analysis]Fix false positive in mutation check when using pointer to member function #66846

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
Sep 25, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
4 changes: 4 additions & 0 deletions clang-tools-extra/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,10 @@ Changes in existing checks
customizable namespace. This further allows for testing the libc when the
system-libc is also LLVM's libc.

- Improved :doc:`misc-const-correctness
<clang-tidy/checks/misc/const-correctness>` check to avoid false positive when
using pointer to member function.

- Improved :doc:`misc-include-cleaner
<clang-tidy/checks/misc/include-cleaner>` check by adding option
`DeduplicateFindings` to output one finding per symbol occurrence.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -976,3 +976,16 @@ void auto_usage_variants() {
auto &auto_td1 = auto_td0;
auto *auto_td2 = &auto_td0;
}

using PointerToMemberFunction = int (Value::*)();
void member_pointer(Value &x, PointerToMemberFunction m) {
Value &member_pointer_tmp = x;
(member_pointer_tmp.*m)();
}

using PointerToConstMemberFunction = int (Value::*)() const;
void member_pointer_const(Value &x, PointerToConstMemberFunction m) {
Value &member_pointer_tmp = x;
// CHECK-MESSAGES:[[@LINE-1]]:3: warning: variable 'member_pointer_tmp' of type 'Value &' can be declared 'const'
(member_pointer_tmp.*m)();
}
3 changes: 3 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,9 @@ Static Analyzer
bitwise shift operators produce undefined behavior (because some operand is
negative or too large).

- Fix false positive in mutation check when using pointer to member function.
(`#66204: <https://github.com/llvm/llvm-project/issues/66204>`_).

- The ``alpha.security.taint.TaintPropagation`` checker no longer propagates
taint on ``strlen`` and ``strnlen`` calls, unless these are marked
explicitly propagators in the user-provided taint configuration file.
Expand Down
22 changes: 19 additions & 3 deletions clang/lib/Analysis/ExprMutationAnalyzer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,20 @@ AST_MATCHER(CXXTypeidExpr, isPotentiallyEvaluated) {
return Node.isPotentiallyEvaluated();
}

AST_MATCHER(CXXMemberCallExpr, isConstCallee) {
const Decl *CalleeDecl = Node.getCalleeDecl();
const auto *VD = dyn_cast_or_null<ValueDecl>(CalleeDecl);
if (!VD)
return false;
const QualType T = VD->getType().getCanonicalType();
const auto *MPT = dyn_cast<MemberPointerType>(T);
const auto *FPT = MPT ? cast<FunctionProtoType>(MPT->getPointeeType())
: dyn_cast<FunctionProtoType>(T);
if (!FPT)
return false;
return FPT->isConst();
}

AST_MATCHER_P(GenericSelectionExpr, hasControllingExpr,
ast_matchers::internal::Matcher<Expr>, InnerMatcher) {
if (Node.isTypePredicate())
Expand Down Expand Up @@ -274,8 +288,8 @@ const Stmt *ExprMutationAnalyzer::findDirectMutation(const Expr *Exp) {
const auto NonConstMethod = cxxMethodDecl(unless(isConst()));

const auto AsNonConstThis = expr(anyOf(
cxxMemberCallExpr(callee(NonConstMethod),
on(canResolveToExpr(equalsNode(Exp)))),
cxxMemberCallExpr(on(canResolveToExpr(equalsNode(Exp))),
unless(isConstCallee())),
cxxOperatorCallExpr(callee(NonConstMethod),
hasArgument(0, canResolveToExpr(equalsNode(Exp)))),
// In case of a templated type, calling overloaded operators is not
Expand Down Expand Up @@ -391,7 +405,9 @@ const Stmt *ExprMutationAnalyzer::findMemberMutation(const Expr *Exp) {
match(findAll(expr(anyOf(memberExpr(hasObjectExpression(
canResolveToExpr(equalsNode(Exp)))),
cxxDependentScopeMemberExpr(hasObjectExpression(
canResolveToExpr(equalsNode(Exp))))))
canResolveToExpr(equalsNode(Exp)))),
binaryOperator(hasOperatorName(".*"),
hasLHS(equalsNode(Exp)))))
.bind(NodeID<Expr>::value)),
Stm, Context);
return findExprMutation(MemberExprs);
Expand Down
30 changes: 30 additions & 0 deletions clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,36 @@ TEST(ExprMutationAnalyzerTest, TypeDependentMemberCall) {
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.push_back(T())"));
}

TEST(ExprMutationAnalyzerTest, MemberPointerMemberCall) {
{
const auto AST =
buildASTFromCode("struct X {};"
"using T = int (X::*)();"
"void f(X &x, T m) { X &ref = x; (ref.*m)(); }");
const auto Results =
match(withEnclosingCompound(declRefTo("ref")), AST->getASTContext());
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("(ref .* m)()"));
}
{
const auto AST =
buildASTFromCode("struct X {};"
"using T = int (X::*)();"
"void f(X &x, T const m) { X &ref = x; (ref.*m)(); }");
const auto Results =
match(withEnclosingCompound(declRefTo("ref")), AST->getASTContext());
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("(ref .* m)()"));
}
{
const auto AST =
buildASTFromCode("struct X {};"
"using T = int (X::*)() const;"
"void f(X &x, T m) { X &ref = x; (ref.*m)(); }");
const auto Results =
match(withEnclosingCompound(declRefTo("ref")), AST->getASTContext());
EXPECT_FALSE(isMutated(Results, AST.get()));
}
}

// Section: overloaded operators

TEST(ExprMutationAnalyzerTest, NonConstOperator) {
Expand Down